Un-pack the 7-display cockpit in-engine (L4MFDSPLIT=1)
The pod drove five monochrome MFDs from the color channels of two video outputs - SVGA16 packs bit-slices of the shared gauge canvas into R/G/B of gauge window 3 (upper MFDs) and R/G of window 4 (lower MFDs), with the map palettized on the secondary and physically mounted portrait. The desktop reconstruction previously required an external BitBlt-mirror wrapper. With L4MFDSPLIT=1, SVGA16 renders each display into its own window (MFDSplitView, plain GDI) straight from the canvas + port bit-masks: five green-screen MFD windows and the 90CW-rotated Map, tiled in the pod grid to the right of the main view (L4MFDSCALE percent, default 50). The packed D3D windows stay hidden but keep presenting off-screen, leaving the original path untouched. Handles spanning mode (2-window setups). Also: the plasma glass now opens directly below the main view (clamped to the work area; L4PLASMAPOS=x,y overrides) per playtest feedback. Verified: window grid comes up as main + 5 MFDs + Map + plasma with the packed windows hidden; screenshots confirm a green MFD score readout and the portrait tactical map rendering correctly. dist packer and BUILD.md updated; the launcher wrapper is obsolete for split-mode use. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -112,7 +112,19 @@ Two new environment options remove the hardware dependency entirely:
|
||||
Hot-plugging works; with no controller it falls back to keyboard only.
|
||||
- **`L4PLASMA=SCREEN`** — renders the 128×32 plasma display as its own
|
||||
desktop window ("Plasma Display", plasma orange, `L4PLASMASCALE` sets the
|
||||
pixel size, default 4). No COM port. Closing the window hides it.
|
||||
pixel size, default 4). It opens directly below the main view;
|
||||
`L4PLASMAPOS=x,y` overrides. No COM port. Closing the window hides it.
|
||||
- **`L4MFDSPLIT=1`** — un-packs the 7-display cockpit. The pod hardware
|
||||
drove five monochrome MFDs from the color channels of two video outputs
|
||||
(window 3 = upper MFDs in R/G/B, window 4 = lower MFDs in R/G) and
|
||||
mounted the map display portrait. With split mode the game itself opens
|
||||
seven proper windows — main view, five green-screen MFDs ("MFD upper
|
||||
left/center/right", "MFD lower left/right") and the 90°-rotated "Map" —
|
||||
rendered CPU-side from the shared gauge canvas; the packed windows stay
|
||||
hidden. `L4MFDSCALE` sets the view size in percent of the gauge canvas
|
||||
(default 50). Default layout is the pod grid to the right of the main
|
||||
view; all windows are draggable. This replaces the external
|
||||
BitBlt-mirror launcher wrapper.
|
||||
|
||||
Bindings (vRIO's default profile, condensed):
|
||||
|
||||
@@ -136,6 +148,7 @@ DPLARG=1
|
||||
L4DPLCFG=RPDPL.INI
|
||||
L4GAUGE=640x480x16
|
||||
L4PLASMA=SCREEN
|
||||
L4MFDSPLIT=1
|
||||
TARGETFPS=60
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#include "mungal4.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "l4mfdview.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const char mfdViewClass[] = "RPMFDView";
|
||||
const char mfdViewProp[] = "RPMFDViewBlit";
|
||||
|
||||
struct MFDViewBlit
|
||||
{
|
||||
BITMAPINFOHEADER header;
|
||||
void *pixels;
|
||||
int sourceWidth;
|
||||
int sourceHeight;
|
||||
};
|
||||
|
||||
LRESULT CALLBACK
|
||||
MFDViewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(hwnd, &ps);
|
||||
const MFDViewBlit *blit =
|
||||
(const MFDViewBlit *) GetPropA(hwnd, mfdViewProp);
|
||||
if (blit != NULL && blit->pixels != NULL)
|
||||
{
|
||||
RECT client;
|
||||
GetClientRect(hwnd, &client);
|
||||
SetStretchBltMode(hdc, COLORONCOLOR);
|
||||
StretchDIBits(
|
||||
hdc,
|
||||
0, 0, client.right, client.bottom,
|
||||
0, 0, blit->sourceWidth, blit->sourceHeight,
|
||||
blit->pixels, (const BITMAPINFO *) &blit->header,
|
||||
DIB_RGB_COLORS, SRCCOPY
|
||||
);
|
||||
}
|
||||
EndPaint(hwnd, &ps);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_CLOSE:
|
||||
// Part of the cockpit; just hide it.
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
return 0;
|
||||
|
||||
case WM_ERASEBKGND:
|
||||
return 1;
|
||||
}
|
||||
return DefWindowProcA(hwnd, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
MFDSplitView::MFDSplitView(
|
||||
const char *title,
|
||||
int source_width,
|
||||
int source_height,
|
||||
int client_width,
|
||||
int client_height,
|
||||
int x,
|
||||
int y
|
||||
)
|
||||
{
|
||||
Check_Pointer(this);
|
||||
|
||||
sourceWidth = source_width;
|
||||
sourceHeight = source_height;
|
||||
|
||||
pixels = new unsigned long[source_width * source_height];
|
||||
memset(pixels, 0, source_width * source_height * sizeof(unsigned long));
|
||||
|
||||
MFDViewBlit *blit = new MFDViewBlit;
|
||||
memset(blit, 0, sizeof(MFDViewBlit));
|
||||
blit->header.biSize = sizeof(BITMAPINFOHEADER);
|
||||
blit->header.biWidth = source_width;
|
||||
blit->header.biHeight = -source_height; // top-down
|
||||
blit->header.biPlanes = 1;
|
||||
blit->header.biBitCount = 32;
|
||||
blit->header.biCompression = BI_RGB;
|
||||
blit->pixels = pixels;
|
||||
blit->sourceWidth = source_width;
|
||||
blit->sourceHeight = source_height;
|
||||
blitInfo = blit;
|
||||
|
||||
HINSTANCE instance = GetModuleHandleA(NULL);
|
||||
|
||||
static Logical class_registered = False;
|
||||
if (!class_registered)
|
||||
{
|
||||
class_registered = True;
|
||||
|
||||
WNDCLASSA window_class;
|
||||
memset(&window_class, 0, sizeof(window_class));
|
||||
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
||||
window_class.lpfnWndProc = MFDViewWndProc;
|
||||
window_class.hInstance = instance;
|
||||
window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
window_class.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
||||
window_class.lpszClassName = mfdViewClass;
|
||||
RegisterClassA(&window_class);
|
||||
}
|
||||
|
||||
DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||
RECT bounds;
|
||||
bounds.left = 0;
|
||||
bounds.top = 0;
|
||||
bounds.right = client_width;
|
||||
bounds.bottom = client_height;
|
||||
AdjustWindowRect(&bounds, style, FALSE);
|
||||
|
||||
window = CreateWindowExA(
|
||||
0,
|
||||
mfdViewClass,
|
||||
title,
|
||||
style,
|
||||
x, y,
|
||||
bounds.right - bounds.left,
|
||||
bounds.bottom - bounds.top,
|
||||
NULL, NULL, instance, NULL
|
||||
);
|
||||
|
||||
if (window != NULL)
|
||||
{
|
||||
SetPropA((HWND) window, mfdViewProp, blitInfo);
|
||||
ShowWindow((HWND) window, SW_SHOWNOACTIVATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_STREAM << "MFDSplitView: window creation failed (" << title << ")\n" << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
MFDSplitView::~MFDSplitView()
|
||||
{
|
||||
Check_Pointer(this);
|
||||
|
||||
if (window != NULL)
|
||||
{
|
||||
RemovePropA((HWND) window, mfdViewProp);
|
||||
DestroyWindow((HWND) window);
|
||||
window = NULL;
|
||||
}
|
||||
delete (MFDViewBlit *) blitInfo;
|
||||
blitInfo = NULL;
|
||||
delete [] pixels;
|
||||
pixels = NULL;
|
||||
}
|
||||
|
||||
Logical
|
||||
MFDSplitView::TestInstance() const
|
||||
{
|
||||
return pixels != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
MFDSplitView::Repaint()
|
||||
{
|
||||
Check_Pointer(this);
|
||||
if (window != NULL)
|
||||
{
|
||||
InvalidateRect((HWND) window, NULL, FALSE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "..\munga\style.h"
|
||||
|
||||
//########################################################################
|
||||
//############################ MFDSplitView ##############################
|
||||
//########################################################################
|
||||
//
|
||||
// One desktop window showing a single cockpit display, used by SVGA16's
|
||||
// split mode (L4MFDSPLIT=1). The pod hardware packed the five monochrome
|
||||
// MFDs into the color channels of two video outputs and mounted the map
|
||||
// display rotated; on a desktop each display gets its own window instead.
|
||||
//
|
||||
// SVGA16 fills Pixels() (32bpp top-down, sourceWidth x sourceHeight as
|
||||
// passed - already swapped by the caller for the rotated map) and calls
|
||||
// Repaint(). Closing a view window just hides it.
|
||||
//
|
||||
class MFDSplitView
|
||||
{
|
||||
public:
|
||||
MFDSplitView(
|
||||
const char *title,
|
||||
int source_width,
|
||||
int source_height,
|
||||
int client_width,
|
||||
int client_height,
|
||||
int x,
|
||||
int y
|
||||
);
|
||||
|
||||
~MFDSplitView();
|
||||
|
||||
Logical
|
||||
TestInstance() const;
|
||||
|
||||
unsigned long *
|
||||
Pixels()
|
||||
{ return pixels; }
|
||||
|
||||
void
|
||||
Repaint();
|
||||
|
||||
protected:
|
||||
void
|
||||
*window; // HWND
|
||||
void
|
||||
*blitInfo; // MFDViewBlit (header + source dims), see .cpp
|
||||
unsigned long
|
||||
*pixels;
|
||||
int
|
||||
sourceWidth,
|
||||
sourceHeight;
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma hdrstop
|
||||
|
||||
#include "l4plasmascreen.h"
|
||||
#include "l4app.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -163,14 +164,52 @@ void
|
||||
bounds.bottom = plasmaHeight * scale;
|
||||
AdjustWindowRect(&bounds, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE);
|
||||
|
||||
int window_w = bounds.right - bounds.left;
|
||||
int window_h = bounds.bottom - bounds.top;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Default position: directly below the main view (the pod mounts
|
||||
// the glass adjacent to the displays). The main window sits at
|
||||
// (0,0) with the -res size; clamp to the work area in case the
|
||||
// main view fills the screen. L4PLASMAPOS=x,y overrides.
|
||||
//---------------------------------------------------------------
|
||||
int x = 0;
|
||||
int y = (int) L4Application::GetScreenHeight();
|
||||
|
||||
const char *pos_string = getenv("L4PLASMAPOS");
|
||||
if (pos_string != NULL)
|
||||
{
|
||||
int px = 0, py = 0;
|
||||
if (sscanf(pos_string, "%d,%d", &px, &py) == 2)
|
||||
{
|
||||
x = px;
|
||||
y = py;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RECT work;
|
||||
if (SystemParametersInfoA(SPI_GETWORKAREA, 0, &work, 0))
|
||||
{
|
||||
if (y + window_h > work.bottom)
|
||||
{
|
||||
y = work.bottom - window_h;
|
||||
}
|
||||
if (y < 0)
|
||||
{
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window = CreateWindowExA(
|
||||
0,
|
||||
plasmaWindowClass,
|
||||
"Plasma Display",
|
||||
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
bounds.right - bounds.left,
|
||||
bounds.bottom - bounds.top,
|
||||
x, y,
|
||||
window_w,
|
||||
window_h,
|
||||
NULL, NULL, instance, NULL
|
||||
);
|
||||
|
||||
|
||||
+171
-1
@@ -2,6 +2,7 @@
|
||||
#pragma hdrstop
|
||||
|
||||
#include "l4vb16.h"
|
||||
#include "l4mfdview.h"
|
||||
#include "../munga/gaugrend.h"
|
||||
#include "L4VIDEO.h"
|
||||
#include "DXUtils.h"
|
||||
@@ -176,7 +177,12 @@ void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed
|
||||
gaugeWindows[j] = CreateWindowEx(0, L"MainWndClass", L"RPL4", WS_BORDER, surfaceWindowRect.left, surfaceWindowRect.top, surfaceWindowRect.right-surfaceWindowRect.left, surfaceWindowRect.bottom-surfaceWindowRect.top, (HWND)NULL, (HMENU)NULL, L4Application::GetAppInstance(), (LPVOID)NULL);
|
||||
if (gaugeWindows[j])
|
||||
{
|
||||
ShowWindow(gaugeWindows[j], SW_SHOW);
|
||||
// In split-view mode the packed windows are only kept as D3D
|
||||
// device targets; the split windows are what the player sees.
|
||||
if (!splitViews)
|
||||
{
|
||||
ShowWindow(gaugeWindows[j], SW_SHOW);
|
||||
}
|
||||
|
||||
memset(&mPresentParams[j], 0, sizeof(D3DPRESENT_PARAMETERS));
|
||||
|
||||
@@ -3809,7 +3815,76 @@ SVGA16::SVGA16(
|
||||
int *aux2Index
|
||||
):Video16BitBuffered(init_width, init_height)
|
||||
{
|
||||
//------------------------------------------------------------------
|
||||
// Split-view mode: decide before BuildWindows so the packed gauge
|
||||
// windows can stay hidden.
|
||||
//------------------------------------------------------------------
|
||||
splitViews = False;
|
||||
{
|
||||
const char *split_string = getenv("L4MFDSPLIT");
|
||||
if (split_string != NULL && atoi(split_string) != 0)
|
||||
{
|
||||
splitViews = True;
|
||||
}
|
||||
}
|
||||
for (int view = 0; view < SplitViewCount; ++view)
|
||||
{
|
||||
splitView[view] = NULL;
|
||||
}
|
||||
|
||||
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
|
||||
|
||||
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 ]
|
||||
//---------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int cell_w = (init_width * scale_percent) / 100;
|
||||
int cell_h = (init_height * scale_percent) / 100;
|
||||
int frame_h = GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYFIXEDFRAME);
|
||||
int frame_w = 2 * GetSystemMetrics(SM_CXFIXEDFRAME);
|
||||
int grid_x = init_width + frame_w;
|
||||
int row2_y = cell_h + frame_h;
|
||||
|
||||
splitView[SplitMFDUpperLeft] = new MFDSplitView(
|
||||
"MFD upper left", init_width, init_height,
|
||||
cell_w, cell_h, grid_x, 0);
|
||||
splitView[SplitMFDUpperCenter] = new MFDSplitView(
|
||||
"MFD upper center", init_width, init_height,
|
||||
cell_w, cell_h, grid_x + (cell_w + frame_w), 0);
|
||||
splitView[SplitMFDUpperRight] = new MFDSplitView(
|
||||
"MFD upper right", init_width, init_height,
|
||||
cell_w, cell_h, grid_x + 2 * (cell_w + frame_w), 0);
|
||||
splitView[SplitMFDLowerLeft] = new MFDSplitView(
|
||||
"MFD lower left", init_width, init_height,
|
||||
cell_w, cell_h, grid_x, row2_y);
|
||||
splitView[SplitMFDLowerRight] = new MFDSplitView(
|
||||
"MFD lower right", init_width, init_height,
|
||||
cell_w, cell_h, grid_x + 2 * (cell_w + frame_w), row2_y);
|
||||
// map is portrait: source rotated 90 degrees clockwise
|
||||
splitView[SplitMap] = new MFDSplitView(
|
||||
"Map", init_height, init_width,
|
||||
cell_h, cell_w,
|
||||
grid_x + (cell_w + frame_w) + (cell_w - cell_h) / 2, row2_y);
|
||||
|
||||
DEBUG_STREAM << "SVGA16: split views active - 5 MFD windows + rotated map (scale "
|
||||
<< scale_percent << "%)\n" << std::flush;
|
||||
}
|
||||
//STUBBED: VIDEO RB 1/15/07
|
||||
# if defined(DEBUG)
|
||||
Tell("SVGA16::SVGA16()\n");
|
||||
@@ -3993,6 +4068,13 @@ SVGA16::~SVGA16()
|
||||
// Set VWE video splitter clock divider
|
||||
//---------------------------------------------------------
|
||||
//SVGASetSplitterClock(False);
|
||||
|
||||
for (int view = 0; view < SplitViewCount; ++view)
|
||||
{
|
||||
delete splitView[view];
|
||||
splitView[view] = NULL;
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
@@ -4029,6 +4111,43 @@ int GetShiftAmount(DWORD mask)
|
||||
return amount;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Render one MFD's bit-slice of the shared canvas into its split-view
|
||||
// window as a green screen. The mask arrives in the duplicated
|
||||
// (mask | mask<<16) form the packed path uses.
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
void SVGA16::FillSplitMFD(
|
||||
SplitViewID view,
|
||||
DWORD mask,
|
||||
int shift
|
||||
)
|
||||
{
|
||||
if (splitView[view] == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Word mask16 = (Word)(mask & 0xFFFF);
|
||||
Word max_value = (Word)(mask16 >> shift);
|
||||
if (max_value == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Word *source = pixelBuffer.Data.MapPointer;
|
||||
unsigned long *dest = splitView[view]->Pixels();
|
||||
int count = pixelBuffer.Data.Size.x * pixelBuffer.Data.Size.y;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
unsigned long value = (unsigned long)((*source++ & mask16) >> shift);
|
||||
unsigned long green = (value * 255ul) / max_value;
|
||||
*dest++ = green << 8;
|
||||
}
|
||||
|
||||
splitView[view]->Repaint();
|
||||
}
|
||||
|
||||
Logical SVGA16::Update(Logical forceAll)
|
||||
{
|
||||
HRESULT hr;
|
||||
@@ -4101,6 +4220,57 @@ Logical SVGA16::Update(Logical forceAll)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Split views: render this window's displays into their own desktop
|
||||
// windows, straight from the shared canvas (CPU-side, no D3D).
|
||||
//------------------------------------------------------------------
|
||||
if (splitViews)
|
||||
{
|
||||
if (mDisplayToUpdate == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
else if (mDisplayToUpdate == 1)
|
||||
{
|
||||
FillSplitMFD(SplitMFDUpperLeft, ulMask, ulMask_sh);
|
||||
FillSplitMFD(SplitMFDUpperCenter, ucMask, ucMask_sh);
|
||||
FillSplitMFD(SplitMFDUpperRight, urMask, urMask_sh);
|
||||
if (NUMGAUGEWINDOWS < 3)
|
||||
{
|
||||
// spanning mode: the lower MFDs ride this window too
|
||||
FillSplitMFD(SplitMFDLowerLeft, llMask, llMask_sh);
|
||||
FillSplitMFD(SplitMFDLowerRight, lrMask, lrMask_sh);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FillSplitMFD(SplitMFDLowerLeft, llMask, llMask_sh);
|
||||
FillSplitMFD(SplitMFDLowerRight, lrMask, lrMask_sh);
|
||||
}
|
||||
}
|
||||
|
||||
Word *data = pixelBuffer.Data.MapPointer;
|
||||
|
||||
D3DLOCKED_RECT rect;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
|
||||
class L4graphicsPort;
|
||||
class MFDSplitView;
|
||||
|
||||
//########################################################################
|
||||
//######################### Video16BitBuffered ###########################
|
||||
@@ -291,6 +292,33 @@ private:
|
||||
RECT *mSurfaceRects;
|
||||
|
||||
int mDisplayToUpdate;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Split-view mode (L4MFDSPLIT=1): the five channel-packed MFDs and
|
||||
// the rotated map render as their own desktop windows; the packed
|
||||
// D3D gauge windows stay hidden. See L4MFDVIEW.h.
|
||||
//------------------------------------------------------------------
|
||||
enum SplitViewID {
|
||||
SplitMFDUpperLeft = 0,
|
||||
SplitMFDUpperCenter,
|
||||
SplitMFDUpperRight,
|
||||
SplitMFDLowerLeft,
|
||||
SplitMFDLowerRight,
|
||||
SplitMap,
|
||||
SplitViewCount
|
||||
};
|
||||
|
||||
void
|
||||
FillSplitMFD(
|
||||
SplitViewID view,
|
||||
DWORD mask16,
|
||||
int shift
|
||||
);
|
||||
|
||||
Logical
|
||||
splitViews;
|
||||
MFDSplitView
|
||||
*splitView[SplitViewCount];
|
||||
};
|
||||
|
||||
//########################################################################
|
||||
|
||||
@@ -249,6 +249,7 @@
|
||||
<ClCompile Include=".\L4MPPR.cpp" />
|
||||
<ClCompile Include=".\L4NET.CPP" />
|
||||
<ClCompile Include=".\L4PARTICLES.cpp" />
|
||||
<ClCompile Include=".\L4MFDVIEW.cpp" />
|
||||
<ClCompile Include=".\L4PADRIO.cpp" />
|
||||
<ClCompile Include=".\L4PCSPAK.cpp" />
|
||||
<ClCompile Include=".\L4PLASMA.cpp" />
|
||||
@@ -438,6 +439,7 @@
|
||||
<ClInclude Include=".\L4MPPR.h" />
|
||||
<ClInclude Include=".\L4NET.H" />
|
||||
<ClInclude Include=".\L4PARTICLES.h" />
|
||||
<ClInclude Include=".\L4MFDVIEW.h" />
|
||||
<ClInclude Include=".\L4PADRIO.h" />
|
||||
<ClInclude Include=".\L4PCSPAK.h" />
|
||||
<ClInclude Include=".\L4PLASMA.h" />
|
||||
|
||||
@@ -564,6 +564,9 @@
|
||||
<ClCompile Include=".\L4PADRIO.cpp">
|
||||
<Filter>Source Files\MUNGA_L4</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include=".\L4MFDVIEW.cpp">
|
||||
<Filter>Source Files\MUNGA_L4</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include=".\L4PLASMASCREEN.cpp">
|
||||
<Filter>Source Files\MUNGA_L4</Filter>
|
||||
</ClCompile>
|
||||
@@ -1127,6 +1130,9 @@
|
||||
<ClInclude Include=".\L4PADRIO.h">
|
||||
<Filter>Header Files\MUNGA_L4</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include=".\L4MFDVIEW.h">
|
||||
<Filter>Header Files\MUNGA_L4</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include=".\L4PLASMASCREEN.h">
|
||||
<Filter>Header Files\MUNGA_L4</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -50,6 +50,12 @@ so press-feedback works like the real button field.
|
||||
`Video8BitBuffered` surface into a desktop "Plasma Display" window in
|
||||
plasma orange — verified drawing live game content (score readout).
|
||||
See BUILD.md §4 for bindings and the desktop `environ.ini`.
|
||||
- ✅ **7-display cockpit in-engine** (`L4MFDSPLIT=1`): the five MFDs the pod
|
||||
packed into the color channels of two video outputs, plus the
|
||||
portrait-mounted map, now open as their own windows (green-screen MFDs,
|
||||
90°-rotated map), rendered CPU-side from the shared gauge canvas — the
|
||||
external BitBlt-mirror wrapper is obsolete. Verified visually (MFD score
|
||||
readout, full tactical map).
|
||||
- Next in A: on-screen RIO panel fed by `PadRIO::lampState[]` (the
|
||||
cockpit-feel layout — vRIO button clusters around the displays); port the
|
||||
full vRIO bindings-file model (deflect/rate/deadzone per axis, rebinding);
|
||||
|
||||
+7
-3
@@ -67,6 +67,7 @@ DPLARG=1
|
||||
L4DPLCFG=RPDPL.INI
|
||||
L4GAUGE=640x480x16
|
||||
L4PLASMA=SCREEN
|
||||
L4MFDSPLIT=1
|
||||
TARGETFPS=60
|
||||
"@
|
||||
|
||||
@@ -94,9 +95,12 @@ Controls (XInput controller and/or keyboard):
|
||||
DPad / arrow keys joystick hat (look)
|
||||
Start,Back / F1,F2 config buttons
|
||||
|
||||
The "Plasma Display" window is the pod's plasma glass. Closing it just
|
||||
hides it. environ.ini options: L4PADFLIP=XY inverts the stick axes,
|
||||
L4PLASMASCALE=n sets the plasma pixel size (default 4).
|
||||
The full pod cockpit comes up as separate windows: the main view, five
|
||||
green MFD screens, the portrait map, and the orange plasma glass below
|
||||
the main view. Drag them wherever you like; closing one just hides it.
|
||||
environ.ini options: L4PADFLIP=XY inverts the stick axes, L4MFDSCALE=n
|
||||
sizes the MFD/map windows (percent, default 50), L4PLASMASCALE=n sets
|
||||
the plasma pixel size (default 4), L4PLASMAPOS=x,y places the plasma.
|
||||
|
||||
Known prototype notes: pods race untextured (the player1-8 skins come
|
||||
from the presets system, not shipped data), and text drawn on the plasma
|
||||
|
||||
Reference in New Issue
Block a user