1920x1080 internal cockpit canvas with pod-bezel occlusion
The cockpit is now a fixed 1920x1080 canvas (uniformly scaled down on
smaller monitors, min 25%):
[ MFD UL 640 ][ MFD UC 640 ][ MFD UR 640 ] <- native glass, 1920 wide
[ viewscreen 800x600 centered ]
[ MFD LL ] [ Map ] [ MFD LR ] <- bottom edge
Playtest direction: the original pods MFDs partially occluded the main
display, and that is used here on purpose - the panes overlap the
viewscreen and render over it (viewscreen pinned to the bottom of the
sibling z-order, WS_CLIPSIBLINGS everywhere, so the 3D present clips
around every pane). Top-row glasses are gauge-canvas native 640x480 -
three across is exactly 1920. Red MFD buttons at half height; the amber
map columns stack contiguous like the pod strips. The auto-fit
scale/L4MFDSCALE machinery is gone - the canvas is the design space.
Verified live at 100% scale: contiguous native top row over the
centered viewscreen, bottom cluster overlapping its lower edge, map and
gauges updating (BOOST values moving, presets lit).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -127,17 +127,23 @@ Two new environment options remove the hardware dependency entirely:
|
||||
[ MFD LL ] [ Map ] [ MFD LR ]
|
||||
```
|
||||
|
||||
MFDs render green-screen and the map full-color/rotated, CPU-side from
|
||||
the shared gauge canvas (the packed D3D windows stay hidden); the 3D
|
||||
scene presents into the viewscreen pane via `Present`'s
|
||||
`hDestWindowOverride` (`gMainPresentWindow`), so no swap-chain changes.
|
||||
The cockpit is a fixed **1920×1080 internal canvas** (scaled down
|
||||
uniformly on smaller work areas): the top row is three
|
||||
native-resolution 640×480 glasses spanning exactly 1920, the 800×600
|
||||
viewscreen (launch with `-res 800 600`) sits centered, and the lower
|
||||
MFDs (corners) + portrait map (bottom center) anchor to the bottom
|
||||
edge. The panes deliberately **overlap the viewscreen and render over
|
||||
it** — in the pod the MFD bezels partially occluded the viewport, and
|
||||
the same trick lets a big 3D view and full-size gauges share the
|
||||
canvas. MFDs render green-screen and the map full-color/rotated,
|
||||
CPU-side from the shared gauge canvas (the packed D3D windows stay
|
||||
hidden); the 3D scene presents into the viewscreen pane via
|
||||
`Present`'s `hDestWindowOverride` (`gMainPresentWindow`), clipped
|
||||
around the panes (viewscreen pinned to the bottom of the z-order).
|
||||
Mouse clicks over the viewscreen fall through to the game window
|
||||
(STATIC pane hit-test transparency). The shell **auto-fits the monitor
|
||||
work area**: the pane scale steps down and the viewscreen shrinks (the
|
||||
present stretches) until the whole cockpit is on screen. `L4MFDSCALE`
|
||||
sets the *requested* MFD/map pane size in percent of the gauge canvas
|
||||
(default 50). The plasma glass is currently hidden in cockpit mode.
|
||||
This replaces the external BitBlt-mirror launcher wrapper.
|
||||
(STATIC pane hit-test transparency). The plasma glass is currently
|
||||
hidden in cockpit mode. This replaces the external BitBlt-mirror
|
||||
launcher wrapper.
|
||||
|
||||
Each split window also carries its display's **physical button bank**
|
||||
(geometry per vRIO's `CockpitLayout`): 4 red buttons above and below
|
||||
|
||||
+23
-10
@@ -165,8 +165,9 @@ MFDSplitView::MFDSplitView(
|
||||
|
||||
if (parent != NULL)
|
||||
{
|
||||
// chrome-less pane inside the cockpit window
|
||||
style = WS_CHILD | WS_VISIBLE;
|
||||
// chrome-less pane inside the cockpit window; clips siblings so
|
||||
// overlapping panes and the viewscreen never paint over it
|
||||
style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
|
||||
window_w = client_width;
|
||||
window_h = client_height;
|
||||
}
|
||||
@@ -253,9 +254,9 @@ void
|
||||
// 4 buttons above the glass, 4 below; RIO addresses descend
|
||||
// from the anchor, row-major (vRIO CockpitLayout::Mfd).
|
||||
//-----------------------------------------------------------
|
||||
int strip_h = display_height / 8;
|
||||
if (strip_h < 18) strip_h = 18;
|
||||
if (strip_h > 40) strip_h = 40;
|
||||
int strip_h = display_height / 16;
|
||||
if (strip_h < 9) strip_h = 9;
|
||||
if (strip_h > 20) strip_h = 20;
|
||||
|
||||
int button_w = (display_width - 5 * buttonGap) / 4;
|
||||
|
||||
@@ -279,15 +280,16 @@ void
|
||||
else if (button_style == SideColumns)
|
||||
{
|
||||
//-----------------------------------------------------------
|
||||
// 6 buttons down each side of the glass (the pod wires only
|
||||
// 6 of each column's 8 addresses to buttons; the rest are
|
||||
// Tesla relays). Left = anchorA ascending, right = anchorB.
|
||||
// 6 buttons down each side of the glass, stacked contiguous
|
||||
// like the pod's strips (the pod wires only 6 of each
|
||||
// column's 8 addresses to buttons; the rest are Tesla
|
||||
// relays). Left = anchorA ascending, right = anchorB.
|
||||
//-----------------------------------------------------------
|
||||
int col_w = display_width / 8;
|
||||
if (col_w < 20) col_w = 20;
|
||||
if (col_w > 40) col_w = 40;
|
||||
|
||||
int button_h = (display_height - 7 * buttonGap) / 6;
|
||||
int button_h = display_height / 6;
|
||||
|
||||
displayX = col_w + 2 * buttonGap;
|
||||
*client_width = display_width + 2 * (col_w + 2 * buttonGap);
|
||||
@@ -298,7 +300,7 @@ void
|
||||
left->unit = anchorA + i;
|
||||
left->amber = 1;
|
||||
left->x = buttonGap;
|
||||
left->y = buttonGap + i * (button_h + buttonGap);
|
||||
left->y = i * button_h;
|
||||
left->w = col_w;
|
||||
left->h = button_h;
|
||||
|
||||
@@ -397,6 +399,17 @@ void
|
||||
// one message per frame, and queued WM_PAINTs (lowest priority) starve
|
||||
// behind it - panes would freeze on their first frame.
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
void
|
||||
MFDSplitView::SetPosition(int x, int y)
|
||||
{
|
||||
Check_Pointer(this);
|
||||
if (window != NULL)
|
||||
{
|
||||
SetWindowPos((HWND) window, NULL, x, y, 0, 0,
|
||||
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MFDSplitView::Repaint()
|
||||
{
|
||||
|
||||
@@ -59,6 +59,12 @@ public:
|
||||
ClientHeight() const
|
||||
{ return clientHeight; }
|
||||
|
||||
// Move the pane (child mode: parent client coords). Panes overlap
|
||||
// the viewscreen like the pod's bezels, so exact placement happens
|
||||
// after construction from the measured client sizes.
|
||||
void
|
||||
SetPosition(int x, int y);
|
||||
|
||||
~MFDSplitView();
|
||||
|
||||
Logical
|
||||
|
||||
+126
-134
@@ -3839,38 +3839,20 @@ SVGA16::SVGA16(
|
||||
if (splitViews)
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
// One window per cockpit display. L4MFDSCALE is the view size in
|
||||
// percent of the gauge canvas (default 50). Default layout is the
|
||||
// pod grid to the right of the main view:
|
||||
// [ main ] [ UL ] [ C ] [ UR ]
|
||||
// [ LL ] [ map ] [ LR ]
|
||||
// The 1920x1080 internal cockpit canvas.
|
||||
//
|
||||
// [ MFD UL 640 ][ MFD UC 640 ][ MFD UR 640 ] <- native glass,
|
||||
// exactly 1920
|
||||
// [ viewscreen 800x600, centered ]
|
||||
// [ MFD LL ] [ Map ] [ MFD LR ] <- bottom edge
|
||||
//
|
||||
// The panes layer OVER the viewscreen like the pod's bezels -
|
||||
// the MFDs partially occlude the main display by design, just
|
||||
// as in the original cockpit. The whole canvas scales down
|
||||
// uniformly on monitors smaller than 1920x1080.
|
||||
//---------------------------------------------------------------
|
||||
int scale_percent = 50;
|
||||
const char *scale_string = getenv("L4MFDSCALE");
|
||||
if (scale_string != NULL)
|
||||
{
|
||||
int requested = atoi(scale_string);
|
||||
if (requested >= 10 && requested <= 200)
|
||||
{
|
||||
scale_percent = requested;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Assemble the pod interior in ONE window (the main game window
|
||||
// becomes the cockpit shell; every display is a chrome-less
|
||||
// child pane):
|
||||
// [ MFD UL ] [ MFD UC ] [ MFD UR ]
|
||||
// [ viewscreen (centered) ]
|
||||
// [ MFD LL ] [ Map ] [ MFD LR ]
|
||||
// The 3D scene presents into the viewscreen child via
|
||||
// gMainPresentWindow. The whole shell AUTO-FITS the monitor work
|
||||
// area: pane scale steps down and the viewscreen shrinks (the
|
||||
// present stretches) until everything is on screen.
|
||||
//---------------------------------------------------------------
|
||||
int gap = 8;
|
||||
int main_w = (int) L4Application::GetScreenWidth();
|
||||
int main_h = (int) L4Application::GetScreenHeight();
|
||||
const int canvas_w = 1920;
|
||||
const int canvas_h = 1080;
|
||||
|
||||
HWND cockpit = ApplicationManager::GetCurrentManager()->GetHWnd();
|
||||
|
||||
@@ -3885,75 +3867,28 @@ SVGA16::SVGA16(
|
||||
}
|
||||
|
||||
RECT work;
|
||||
work.left = 0; work.top = 0; work.right = 1600; work.bottom = 900;
|
||||
work.left = 0; work.top = 0; work.right = 1920; work.bottom = 1080;
|
||||
SystemParametersInfoA(SPI_GETWORKAREA, 0, &work, 0);
|
||||
int avail_w = (work.right - work.left) - chrome_w;
|
||||
int avail_h = (work.bottom - work.top) - chrome_h;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Fit: step the pane scale down until the rows fit the width
|
||||
// and leave a usable viewscreen height. Pane sizes mirror
|
||||
// MFDSplitView::LayoutButtons (buttonGap = 4).
|
||||
//---------------------------------------------------------------
|
||||
int scale = scale_percent;
|
||||
int cell_w, cell_h, strip_h, block_h;
|
||||
int map_col_w, map_block_w, map_block_h;
|
||||
int row_w, col_span, lower_h;
|
||||
int view_w, view_h;
|
||||
|
||||
for (;;)
|
||||
int scale_num = 100;
|
||||
if (avail_w < canvas_w || avail_h < canvas_h)
|
||||
{
|
||||
cell_w = (init_width * scale) / 100;
|
||||
cell_h = (init_height * scale) / 100;
|
||||
int fit_w = (avail_w * 100) / canvas_w;
|
||||
int fit_h = (avail_h * 100) / canvas_h;
|
||||
scale_num = (fit_w < fit_h) ? fit_w : fit_h;
|
||||
if (scale_num < 25) scale_num = 25;
|
||||
|
||||
strip_h = cell_h / 8;
|
||||
if (strip_h < 18) strip_h = 18;
|
||||
if (strip_h > 40) strip_h = 40;
|
||||
block_h = cell_h + 2 * (strip_h + 8);
|
||||
|
||||
map_col_w = cell_h / 8;
|
||||
if (map_col_w < 20) map_col_w = 20;
|
||||
if (map_col_w > 40) map_col_w = 40;
|
||||
map_block_w = cell_h + 2 * (map_col_w + 8);
|
||||
map_block_h = cell_w;
|
||||
|
||||
col_span = cell_w + gap;
|
||||
row_w = 3 * cell_w + 2 * gap;
|
||||
lower_h = (block_h > map_block_h) ? block_h : map_block_h;
|
||||
|
||||
// viewscreen gets the leftover height, at most native size,
|
||||
// keeping the 3D aspect
|
||||
view_h = avail_h - block_h - lower_h - 2 * gap;
|
||||
if (view_h > main_h) view_h = main_h;
|
||||
view_w = (int)((__int64) main_w * view_h / main_h);
|
||||
if (view_w > avail_w)
|
||||
{
|
||||
view_w = avail_w;
|
||||
view_h = (int)((__int64) main_h * view_w / main_w);
|
||||
}
|
||||
|
||||
if ((row_w <= avail_w && view_h >= 240) || scale <= 20)
|
||||
{
|
||||
break;
|
||||
}
|
||||
scale -= 5;
|
||||
}
|
||||
|
||||
if (scale != scale_percent)
|
||||
{
|
||||
DEBUG_STREAM << "SVGA16: pane scale reduced to " << scale
|
||||
<< "% to fit the " << (work.right - work.left) << "x"
|
||||
DEBUG_STREAM << "SVGA16: cockpit canvas scaled to " << scale_num
|
||||
<< "% for the " << (work.right - work.left) << "x"
|
||||
<< (work.bottom - work.top) << " work area\n" << std::flush;
|
||||
}
|
||||
|
||||
int client_w = (row_w > view_w) ? row_w : view_w;
|
||||
int client_h = block_h + gap + view_h + gap + lower_h;
|
||||
int rows_x = (client_w - row_w) / 2;
|
||||
if (rows_x < 0) rows_x = 0;
|
||||
int view_x = (client_w - view_w) / 2;
|
||||
if (view_x < 0) view_x = 0;
|
||||
int band_y = block_h + gap;
|
||||
int lower_y = band_y + view_h + gap;
|
||||
#define COCKPIT_CANVAS(v) (((v) * scale_num) / 100)
|
||||
|
||||
int client_w = COCKPIT_CANVAS(canvas_w);
|
||||
int client_h = COCKPIT_CANVAS(canvas_h);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Grow the main window into the cockpit shell
|
||||
@@ -3969,51 +3904,22 @@ SVGA16::SVGA16(
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Button banks per display (addresses per vRIO CockpitLayout,
|
||||
// bank-to-display placement per the pod cockpit):
|
||||
// upper left MFD 0x2F.. / upper center 0x27.. / upper right
|
||||
// 0x37.. / lower left 0x0F.. / lower right 0x07.. and the map
|
||||
// flanked by Secondary 0x10-0x15 / Screen 0x18-0x1D.
|
||||
// The viewscreen: created FIRST so every pane sits above it in
|
||||
// the z-order, and clipped by those siblings so the 3D present
|
||||
// never draws over them. STATIC hit-tests transparent, so mouse
|
||||
// input over the 3D view reaches the game window as before.
|
||||
// 800x600 (the -res native size), centered on the canvas.
|
||||
//---------------------------------------------------------------
|
||||
splitView[SplitMFDUpperLeft] = new MFDSplitView(
|
||||
"MFD upper left", init_width, init_height,
|
||||
cell_w, cell_h, rows_x, 0,
|
||||
MFDSplitView::MFDStrips, 0x2F, 0, cockpit);
|
||||
splitView[SplitMFDUpperCenter] = new MFDSplitView(
|
||||
"MFD upper center", init_width, init_height,
|
||||
cell_w, cell_h, rows_x + col_span, 0,
|
||||
MFDSplitView::MFDStrips, 0x27, 0, cockpit);
|
||||
splitView[SplitMFDUpperRight] = new MFDSplitView(
|
||||
"MFD upper right", init_width, init_height,
|
||||
cell_w, cell_h, rows_x + 2 * col_span, 0,
|
||||
MFDSplitView::MFDStrips, 0x37, 0, cockpit);
|
||||
splitView[SplitMFDLowerLeft] = new MFDSplitView(
|
||||
"MFD lower left", init_width, init_height,
|
||||
cell_w, cell_h, rows_x, lower_y,
|
||||
MFDSplitView::MFDStrips, 0x0F, 0, cockpit);
|
||||
splitView[SplitMFDLowerRight] = new MFDSplitView(
|
||||
"MFD lower right", init_width, init_height,
|
||||
cell_w, cell_h, rows_x + 2 * col_span, lower_y,
|
||||
MFDSplitView::MFDStrips, 0x07, 0, cockpit);
|
||||
// map is portrait: source rotated 90 degrees clockwise, its pane
|
||||
// centered under the middle column
|
||||
splitView[SplitMap] = new MFDSplitView(
|
||||
"Map", init_height, init_width,
|
||||
cell_h, cell_w,
|
||||
rows_x + col_span + (cell_w - map_block_w) / 2, lower_y,
|
||||
MFDSplitView::SideColumns, 0x10, 0x18, cockpit);
|
||||
int view_w = COCKPIT_CANVAS(800);
|
||||
int view_h = COCKPIT_CANVAS(600);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// The viewscreen: a black STATIC pane the 3D scene presents
|
||||
// into. STATIC hit-tests transparent, so mouse input over the
|
||||
// 3D view still reaches the game window as before.
|
||||
//---------------------------------------------------------------
|
||||
if (cockpit != NULL)
|
||||
{
|
||||
cockpitViewscreen = CreateWindowExA(
|
||||
0, "STATIC", "",
|
||||
WS_CHILD | WS_VISIBLE | SS_BLACKRECT,
|
||||
view_x, band_y, view_w, view_h,
|
||||
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_BLACKRECT,
|
||||
(client_w - view_w) / 2, (client_h - view_h) / 2,
|
||||
view_w, view_h,
|
||||
cockpit, NULL, GetModuleHandleA(NULL), NULL);
|
||||
if (cockpitViewscreen != NULL)
|
||||
{
|
||||
@@ -4021,14 +3927,100 @@ SVGA16::SVGA16(
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Button banks per display (addresses per vRIO CockpitLayout,
|
||||
// bank-to-display placement per the pod cockpit):
|
||||
// upper left MFD 0x2F.. / upper center 0x27.. / upper right
|
||||
// 0x37.. / lower left 0x0F.. / lower right 0x07.. and the map
|
||||
// flanked by Secondary 0x10-0x15 / Screen 0x18-0x1D.
|
||||
//---------------------------------------------------------------
|
||||
int top_glass_w = COCKPIT_CANVAS(640);
|
||||
int top_glass_h = COCKPIT_CANVAS(480);
|
||||
int bot_glass_w = COCKPIT_CANVAS(320);
|
||||
int bot_glass_h = COCKPIT_CANVAS(240);
|
||||
int map_glass_w = COCKPIT_CANVAS(240);
|
||||
int map_glass_h = COCKPIT_CANVAS(320);
|
||||
|
||||
splitView[SplitMFDUpperLeft] = new MFDSplitView(
|
||||
"MFD upper left", init_width, init_height,
|
||||
top_glass_w, top_glass_h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x2F, 0, cockpit);
|
||||
splitView[SplitMFDUpperCenter] = new MFDSplitView(
|
||||
"MFD upper center", init_width, init_height,
|
||||
top_glass_w, top_glass_h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x27, 0, cockpit);
|
||||
splitView[SplitMFDUpperRight] = new MFDSplitView(
|
||||
"MFD upper right", init_width, init_height,
|
||||
top_glass_w, top_glass_h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x37, 0, cockpit);
|
||||
splitView[SplitMFDLowerLeft] = new MFDSplitView(
|
||||
"MFD lower left", init_width, init_height,
|
||||
bot_glass_w, bot_glass_h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x0F, 0, cockpit);
|
||||
splitView[SplitMFDLowerRight] = new MFDSplitView(
|
||||
"MFD lower right", init_width, init_height,
|
||||
bot_glass_w, bot_glass_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,
|
||||
map_glass_w, map_glass_h, 0, 0,
|
||||
MFDSplitView::SideColumns, 0x10, 0x18, cockpit);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Place the panes from their measured sizes: top row edge to
|
||||
// edge across the canvas, bottom cluster on the bottom edge.
|
||||
//---------------------------------------------------------------
|
||||
if (splitView[SplitMFDUpperLeft] != NULL)
|
||||
{
|
||||
splitView[SplitMFDUpperLeft]->SetPosition(0, 0);
|
||||
}
|
||||
if (splitView[SplitMFDUpperCenter] != NULL)
|
||||
{
|
||||
splitView[SplitMFDUpperCenter]->SetPosition(
|
||||
(client_w - splitView[SplitMFDUpperCenter]->ClientWidth()) / 2, 0);
|
||||
}
|
||||
if (splitView[SplitMFDUpperRight] != NULL)
|
||||
{
|
||||
splitView[SplitMFDUpperRight]->SetPosition(
|
||||
client_w - splitView[SplitMFDUpperRight]->ClientWidth(), 0);
|
||||
}
|
||||
if (splitView[SplitMFDLowerLeft] != NULL)
|
||||
{
|
||||
splitView[SplitMFDLowerLeft]->SetPosition(
|
||||
0, client_h - splitView[SplitMFDLowerLeft]->ClientHeight());
|
||||
}
|
||||
if (splitView[SplitMFDLowerRight] != NULL)
|
||||
{
|
||||
splitView[SplitMFDLowerRight]->SetPosition(
|
||||
client_w - splitView[SplitMFDLowerRight]->ClientWidth(),
|
||||
client_h - splitView[SplitMFDLowerRight]->ClientHeight());
|
||||
}
|
||||
if (splitView[SplitMap] != NULL)
|
||||
{
|
||||
splitView[SplitMap]->SetPosition(
|
||||
(client_w - splitView[SplitMap]->ClientWidth()) / 2,
|
||||
client_h - splitView[SplitMap]->ClientHeight());
|
||||
}
|
||||
|
||||
// The panes go OVER the main display (pod bezel occlusion): pin
|
||||
// the viewscreen to the bottom of the sibling z-order so the 3D
|
||||
// present clips around every pane.
|
||||
if (cockpitViewscreen != NULL)
|
||||
{
|
||||
SetWindowPos(cockpitViewscreen, HWND_BOTTOM, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
// the plasma glass sits out for now
|
||||
PlasmaScreen::Hide();
|
||||
|
||||
DEBUG_STREAM << "SVGA16: single-window cockpit assembled ("
|
||||
<< client_w << "x" << client_h << " in "
|
||||
<< (work.right - work.left) << "x" << (work.bottom - work.top)
|
||||
<< " work area, scale " << scale << "%, viewscreen "
|
||||
<< view_w << "x" << view_h << ")\n" << std::flush;
|
||||
<< client_w << "x" << client_h << " canvas at " << scale_num
|
||||
<< "%, viewscreen " << view_w << "x" << view_h
|
||||
<< " centered)\n" << std::flush;
|
||||
|
||||
#undef COCKPIT_CANVAS
|
||||
}
|
||||
//STUBBED: VIDEO RB 1/15/07
|
||||
# if defined(DEBUG)
|
||||
|
||||
Reference in New Issue
Block a user