Under BT_DEV_GAUGES the DEFAULT is now the pod-faithful cockpit surround: the 3D world CENTERED with the six gauge surfaces composited AROUND it in the single main window at 1/2 native scale, plus clickable RIO button lamps. Iterated as a visual reference with the user, then landed in the engine. - L4VB16.cpp/.h: BTCockpitLayout + BTCockpitComputeLayout (layout single source of truth, computed from the backbuffer canvas); BTDrawCockpitPanels (band fill -> button lamp quads -> 9-entry surface loop, phosphor-green mono / amber palette sec, Eng-sibling slot map -> GDI-atlas flight labels; D3DSBT_ALL state-block isolation = the floating-rocks trap); BTCockpitMouseDown/Up (client->bb hit-test + glass press/release/right-latch contract); BTApplyWorldViewport + DevGaugeDocked cockpit branches; shared BTLampBrightnessOf inline. Hooked at the top of BTDrawGaugeInset (before BT_SHOT, so shots include it). - btl4main.cpp: glass preset (cockpit default, per-display panels opt-in); mode resolution -> gBTGaugeCockpit with work-area-clamped window sizing (-res = view size); WndProc WM_L/RBUTTON routing. - L4VIDEO.cpp: BTWorldAspectOf cockpit branch (view rect on-screen aspect). - L4VIDRND.cpp: CameraShipHUDRenderable::Render made viewport-relative. Buttons = the L4GLASSWIN address banks x0.5 (Heat 0x2F, Mfd2 0x27, Comm 0x37, Mfd1 0x0F, Mfd3 0x07 red; radar rails 0x10-0x1B yellow; flight 0x38-0x47 blue, labeled). Full rect is the hit target; the surface draws over it so only the lamp edge shows. Precedence: BT_GLASS_PANELS=1 stands cockpit down > BT_COCKPIT=1 > _WINDOW/_DOCK opt-out > cockpit default; BT_COCKPIT=0 = dock-bottom. Green tunable via BT_COCKPIT_TINT. Renders in ALL builds; only the PadRIO click/lamp seam is BT_GLASS-gated. Verified (awaiting playtest): both build/ + build-glass/ compile (0 error C); glass BT_SHOT matches the mockup; 500-click storm during a live mission survived (Gitea #18 Receiver-gap fix holds); BT_COCKPIT=0 dock-bottom unregressed; BT_GLASS_PANELS=1 stands cockpit down; pod build renders the panels. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
613 lines
14 KiB
C++
613 lines
14 KiB
C++
#pragma once
|
|
|
|
#include "..\munga\graph2d.h"
|
|
#include "..\munga\time.h"
|
|
#include <D3DX9.h>
|
|
#include <D3DX9math.h>
|
|
|
|
|
|
class L4graphicsPort;
|
|
class GaugeRenderer;
|
|
|
|
// GLASS: resolve the gauge renderer / main window. DEFINED in game/btl4main.cpp
|
|
// off the REAL app + window pointers it holds as locals -- NOT off the
|
|
// `application`/`ghWnd` globals, which are duplicate-defined and bind
|
|
// non-deterministically per link under /FORCE (the "duplicate-symbol race" --
|
|
// CMakeLists.txt:186), so a newly-added engine TU (L4GLASSWIN) can read NULL.
|
|
GaugeRenderer *BTResolveGaugeRenderer();
|
|
void *BTResolveMainWindow(); // HWND (kept void* -- no windows.h dependency)
|
|
|
|
//########################################################################
|
|
//#################### COCKPIT SURROUND (BT_COCKPIT) #####################
|
|
//########################################################################
|
|
//
|
|
// The pod-faithful single-window layout: the 3D world view CENTERED, with the
|
|
// six live gauge surfaces composited AROUND it at 1/2 native scale (the pod
|
|
// monitors physically clip the eyeport, so the corner MFDs / top-center / etc.
|
|
// overlap the view edges) and clickable RIO button lamps. DEFAULT under
|
|
// BT_DEV_GAUGES (BT_COCKPIT=0 falls back to the dock-bottom strip).
|
|
//
|
|
// The layout is the SINGLE SOURCE OF TRUTH -- computed from the backbuffer
|
|
// (canvas) size and consumed by the window sizing, the world viewport, the
|
|
// panel/button draw, the aspect calc, and the mouse hit-test. See L4VB16.cpp.
|
|
//
|
|
struct BTCockpitBtn
|
|
{
|
|
int address; // RIO buttonGroup address (0x00-0x47)
|
|
int x, y, w, h; // backbuffer-space rect (full hit target; only the
|
|
// protruding lamp edge shows after the surface draws over)
|
|
int colorClass; // 0 = red (MFD), 1 = yellow (radar), 2 = blue (flight)
|
|
int inert; // 1 = no authored .CTL mapping -> drawn greyed
|
|
const char *label; // non-NULL (blue flight block) -> face is drawn + labeled
|
|
};
|
|
|
|
struct BTCockpitLayout
|
|
{
|
|
int canvasW, canvasH;
|
|
int viewX, viewY, viewW, viewH; // centered world view
|
|
int surfX[6], surfY[6], surfW[6], surfH[6]; // Heat,Mfd2,Mfd1,Mfd3,sec,Comm dest rects
|
|
BTCockpitBtn buttons[80]; // 5*8 red + 16 yellow + 16 blue = 72
|
|
int buttonCount;
|
|
};
|
|
|
|
extern int gBTGaugeCockpit; // mode select; set once by btl4main
|
|
extern int gBTCockpitCanvasW, gBTCockpitCanvasH; // backbuffer canvas dims (for the aspect calc)
|
|
|
|
void BTCockpitCanvasFor(int viewW, int viewH, int *canvasW, int *canvasH);
|
|
void BTCockpitComputeLayout(int canvasW, int canvasH, BTCockpitLayout *out);
|
|
void BTDrawCockpitPanels(LPDIRECT3DDEVICE9 device);
|
|
int BTCockpitMouseDown(int cx, int cy, int clientW, int clientH, int rightButton); // 1 = consumed
|
|
void BTCockpitMouseUp(void);
|
|
|
|
// Lamp brightness decode (RIOBase::LampState) -- shared by the cockpit draw and
|
|
// the glass per-display windows. level = max of the two 2-bit fields; the low 2
|
|
// bits pick a flash half-period (500/250/125 ms). Returns 0..3.
|
|
inline int BTLampBrightnessOf(int state, unsigned long tick)
|
|
{
|
|
int level1 = (state >> 2) & 0x3;
|
|
int level2 = (state >> 4) & 0x3;
|
|
int level = (level1 > level2) ? level1 : level2;
|
|
if (level == 0) return 0;
|
|
int flash = state & 0x3;
|
|
if (flash != 0)
|
|
{
|
|
unsigned long half = (flash == 1) ? 500 : (flash == 2) ? 250 : 125;
|
|
if ((tick / half) & 1) return 0;
|
|
}
|
|
return level;
|
|
}
|
|
|
|
//########################################################################
|
|
//######################### Video16BitBuffered ###########################
|
|
//########################################################################
|
|
//
|
|
// X/Y values passed to these routines are assumed to be in
|
|
// the VIDEO DISPLAY COORDINATE SYSTEM, with (0,0) at the top left.
|
|
//
|
|
class Video16BitBuffered :
|
|
public GraphicsDisplay
|
|
{
|
|
public:
|
|
Video16BitBuffered(int x, int y);
|
|
|
|
~Video16BitBuffered();
|
|
|
|
Logical
|
|
TestInstance() const;
|
|
|
|
void
|
|
ShowInstance(char *indent);
|
|
|
|
void
|
|
DrawPoint(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int x, int y
|
|
);
|
|
|
|
void
|
|
DrawLine(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int x1, int y1,
|
|
int x2, int y2,
|
|
Logical include_last_pixel
|
|
);
|
|
|
|
void
|
|
DrawFilledRectangle(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int x1, int y1,
|
|
int x2, int y2
|
|
);
|
|
|
|
void
|
|
DrawText(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
Logical opaque,
|
|
int rotation,
|
|
Enumeration fontNumber,
|
|
Logical vertical,
|
|
GraphicsDisplay::Justification justification,
|
|
Rectangle2D *clippingRectanglePtr,
|
|
char *stringPointer
|
|
);
|
|
|
|
void
|
|
DrawBitMap(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
BitMap *bitmap,
|
|
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
|
|
);
|
|
|
|
void
|
|
DrawBitMapOpaque(
|
|
int color,
|
|
int background,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
BitMap *bitmap,
|
|
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
|
|
);
|
|
|
|
void
|
|
DrawPixelMap8(
|
|
int *translation_table,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
Logical opaque,
|
|
int rotation,
|
|
int x, int y,
|
|
PixelMap8 *pixelmap,
|
|
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
|
|
);
|
|
|
|
void
|
|
DrawPixelMap8SingleColor(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
PixelMap8 *pixelmap,
|
|
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
|
|
);
|
|
|
|
protected:
|
|
void
|
|
MarkChangedLines(int screenTop, int screenBottom);
|
|
|
|
void
|
|
buildDestPointer(
|
|
int x, int y,
|
|
Word **pixelPointer,
|
|
LWord **changed_bit_pointer,
|
|
LWord *changed_bit
|
|
);
|
|
|
|
Logical
|
|
valid;
|
|
int
|
|
width,
|
|
changedBitWidth,
|
|
height,
|
|
maximumX,
|
|
maximumY;
|
|
|
|
PixelMap16
|
|
pixelBuffer;
|
|
Byte
|
|
*changedLine;
|
|
LWord
|
|
*changedBit;
|
|
};
|
|
|
|
|
|
//########################################################################
|
|
//################################ SVGA16 ################################
|
|
//########################################################################
|
|
struct SVGA16Palette
|
|
{
|
|
Word
|
|
hardwarePort;
|
|
Logical
|
|
modified;
|
|
Palette8
|
|
paletteData;
|
|
Scalar
|
|
flashAccumulator,
|
|
flashRate;
|
|
|
|
enum PaletteID
|
|
{
|
|
maskStates=4
|
|
};
|
|
unsigned char
|
|
mask[maskStates];
|
|
int
|
|
previousMaskState;
|
|
};
|
|
|
|
|
|
class SVGA16 :
|
|
public Video16BitBuffered
|
|
{
|
|
friend class L4GraphicsPort; // for palette access
|
|
|
|
public:
|
|
enum PaletteID {
|
|
NativePalette=0,
|
|
SecondaryPalette,
|
|
AuxiliaryPalette1,
|
|
AuxiliaryPalette2,
|
|
PaletteCount
|
|
};
|
|
|
|
SVGA16(
|
|
int mode,
|
|
int width,
|
|
int height,
|
|
int page_size,
|
|
int mem_granularity,
|
|
int bytes_per_line,
|
|
int page_function_pointer,
|
|
int special_interface,
|
|
bool windowed,
|
|
int *secondaryIndex,
|
|
int *aux1Index,
|
|
int *aux2Index
|
|
);
|
|
|
|
~SVGA16();
|
|
|
|
Logical
|
|
TestInstance() const;
|
|
|
|
void
|
|
ShowInstance(char *indent);
|
|
|
|
Logical
|
|
Update(Logical forceall);
|
|
void
|
|
Refresh();
|
|
void
|
|
UpdatePalette();
|
|
|
|
void
|
|
FadeToPalettes(Scalar fade_time);
|
|
void
|
|
FadeToWhite(Scalar fade_time);
|
|
void
|
|
FlashPalette(
|
|
int palette_number,
|
|
Scalar rate,
|
|
unsigned char *stateList
|
|
);
|
|
void
|
|
UnflashPalette(int palette_number);
|
|
|
|
void
|
|
FunkyVideo(Logical on_off);
|
|
|
|
protected:
|
|
|
|
void BuildWindows(unsigned int width, unsigned int height, bool windowed, int *secondaryIndex, int *aux1Gauge, int *aux2Gauge);
|
|
|
|
|
|
void
|
|
ResetUpdatePosition();
|
|
|
|
int
|
|
pageSize,
|
|
pageDelta,
|
|
widthInBytes,
|
|
pageFcnPtr,
|
|
specialInterface;
|
|
|
|
int
|
|
currentPageNumber,
|
|
nextPageNumber,
|
|
lineNumber;
|
|
long
|
|
destOffset;
|
|
Byte
|
|
*changedLinePointer;
|
|
Word
|
|
*sourcePointer;
|
|
LWord
|
|
*changedBitPointer;
|
|
|
|
enum PaletteFadeState
|
|
{
|
|
staticPalette,
|
|
fadeToWhite,
|
|
whitePalette,
|
|
fadeToColor
|
|
};
|
|
|
|
PaletteFadeState
|
|
paletteFadeState;
|
|
Scalar
|
|
fadeAlpha,
|
|
fadeUnitsPerSecond;
|
|
Time
|
|
previousFadeTime;
|
|
|
|
// This array is closely linked to the PaletteID enumeration, above
|
|
SVGA16Palette
|
|
palette[PaletteCount];
|
|
|
|
private:
|
|
int NUMGAUGEWINDOWS;
|
|
HWND *gaugeWindows;
|
|
LPDIRECT3DDEVICE9 *mDevice;
|
|
D3DPRESENT_PARAMETERS *mPresentParams;
|
|
LPDIRECT3DVERTEXBUFFER9 *mVertBuffers;
|
|
LPDIRECT3DTEXTURE9 *mSurfaces;
|
|
DWORD *mLockFlags;
|
|
RECT *mSurfaceRects;
|
|
|
|
int mDisplayToUpdate;
|
|
|
|
LPDIRECT3DTEXTURE9 mDevSurfaceTex[10]; // DEV-COMPOSITE: one texture per gauge surface
|
|
// (sec/radar + the 5 MFD bit-planes + the Eng1-3
|
|
// preset-page planes, Gitea #9), on the MAIN device.
|
|
public:
|
|
// DEV-COMPOSITE: extract ONE gauge surface from the shared pixelBuffer into
|
|
// mDevSurfaceTex[slot] on the given (main) device + draw it as a quad at (dstX,dstY,
|
|
// dstW,dstH). monoTint < 0 -> palette-expand (sec/radar, case-0 LUT); monoTint >= 0
|
|
// -> mono bit-plane expand ((word & mask) ? tint : 0) for an MFD plane. See
|
|
// BTDrawGaugeSurfaces (L4VB16.cpp).
|
|
// rotateCCW: 0 = as-is; 1/3 = display the texture turned 90 degrees (the
|
|
// pod's portrait-mounted secondary CRT; the dev panel unrotates it).
|
|
void DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int paletteID,
|
|
int monoTint, float dstX, float dstY, float dstW, float dstH,
|
|
int rotateCCW = 0);
|
|
|
|
// GLASS (per-display windows, L4GLASSWIN): CPU-expand ONE gauge surface (a
|
|
// bit-plane of the shared pixelBuffer) into a 32-bit BGRA image for GDI
|
|
// StretchDIBits -- no D3D. Same pixel rule as DrawDevSurface: monoTint < 0 ->
|
|
// palette-expand via palette[paletteID]; else fill (word & mask) ? monoTint : 0
|
|
// (monoTint is a 0x00RRGGBB value here, NOT R5G6B5). rotateQuadrant: 0 = native
|
|
// (outW=bufW, outH=bufH); 1 = 90 CCW / 3 = 90 CW -> transposed (outW=bufH,
|
|
// outH=bufW), for the portrait secondary CRT. `dst` must hold >= bufW*bufH
|
|
// dwords; the image is written TOP-DOWN. *outW/*outH receive the produced size.
|
|
void ExpandPlaneToBGRA(int mask, int paletteID, int monoTint, int rotateQuadrant,
|
|
unsigned long *dst, int *outW, int *outH);
|
|
};
|
|
|
|
//########################################################################
|
|
//########################## L4GraphicsPort ##############################
|
|
//########################################################################
|
|
//
|
|
// X/Y values passed to these routines are assumed to be in
|
|
// the GRAPHICS COORDINATE SYSTEM, with (0,0) at the bottom left.
|
|
//
|
|
class L4GraphicsPort :
|
|
public GraphicsPort
|
|
{
|
|
public:
|
|
enum ChannelEnableID {
|
|
RedChannel,
|
|
GreenChannel,
|
|
BlueChannel,
|
|
AllChannels,
|
|
|
|
DirectColor,
|
|
BlankColor,
|
|
|
|
RedChannelTransparentZero=0x80,
|
|
GreenChannelTransparentZero,
|
|
BlueChannelTransparentZero,
|
|
AllChannelsTransparentZero
|
|
};
|
|
|
|
L4GraphicsPort(
|
|
Video16BitBuffered *graphics_display,
|
|
const char *name,
|
|
int rotation,
|
|
int bit_mask,
|
|
SVGA16::PaletteID palette_ID,
|
|
L4GraphicsPort::ChannelEnableID channel_enable
|
|
);
|
|
|
|
~L4GraphicsPort();
|
|
|
|
Logical
|
|
TestInstance() const;
|
|
|
|
void
|
|
ShowInstance(char *indent);
|
|
|
|
void
|
|
SetSecondaryPalette(
|
|
Palette8 *source_palette
|
|
);
|
|
|
|
void
|
|
SetAuxiliaryPalette();
|
|
|
|
void
|
|
SetColor(
|
|
PaletteTriplet *source_color,
|
|
int color_index
|
|
);
|
|
|
|
void
|
|
DrawPoint(
|
|
int color,
|
|
Enumeration operation,
|
|
int x, int y
|
|
);
|
|
|
|
void
|
|
DrawLine(
|
|
int color,
|
|
Enumeration operation,
|
|
int x1, int y1,
|
|
int x2, int y2,
|
|
Logical include_last_pixel
|
|
);
|
|
|
|
void
|
|
DrawFilledRectangle(
|
|
int color,
|
|
Enumeration operation,
|
|
int x1, int y1,
|
|
int x2, int y2
|
|
);
|
|
|
|
void
|
|
DrawText(
|
|
int color,
|
|
Enumeration operation,
|
|
Logical opaque,
|
|
int rotation,
|
|
Enumeration fontNumber,
|
|
Logical vertical,
|
|
GraphicsDisplay::Justification justification,
|
|
Rectangle2D *clippingRectanglePtr,
|
|
char *stringPointer
|
|
);
|
|
|
|
void
|
|
DrawBitMap(
|
|
int color,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
BitMap *bitmap,
|
|
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
|
|
);
|
|
|
|
void
|
|
DrawBitMapOpaque(
|
|
int color,
|
|
int background,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
BitMap *bitmap,
|
|
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
|
|
);
|
|
|
|
void
|
|
DrawPixelMap8(
|
|
Enumeration operation,
|
|
Logical opaque,
|
|
int rotation,
|
|
int x, int y,
|
|
PixelMap8 *pixelmap,
|
|
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
|
|
);
|
|
|
|
void
|
|
DrawPixelMap8SingleColor(
|
|
int color,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
PixelMap8 *pixelmap,
|
|
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
|
|
);
|
|
|
|
void
|
|
SetPaletteID(SVGA16::PaletteID palette_ID)
|
|
{
|
|
Check(this);
|
|
paletteID = palette_ID;
|
|
}
|
|
|
|
void
|
|
SetEnableID(L4GraphicsPort::ChannelEnableID channel_enable)
|
|
{
|
|
Check(this);
|
|
channelEnable = channel_enable;
|
|
}
|
|
|
|
int
|
|
GetBitMask()
|
|
{
|
|
Check(this);
|
|
return bitMask;
|
|
}
|
|
// DEV-COMPOSITE (Gitea #9): the dock honors the live reconfigure state --
|
|
// a plane whose channel is currently BlankColor contributes nothing on the
|
|
// pod's shared-word palette LUT, so the dev panel skips drawing it.
|
|
L4GraphicsPort::ChannelEnableID
|
|
GetEnableID()
|
|
{
|
|
Check(this);
|
|
return channelEnable;
|
|
}
|
|
SVGA16::PaletteID
|
|
paletteID;
|
|
|
|
protected:
|
|
void
|
|
convertPortPair(
|
|
int *xp,
|
|
int *yp,
|
|
int x,
|
|
int y
|
|
);
|
|
|
|
void
|
|
BuildDirectTranslation(
|
|
Palette8 *source_palette
|
|
);
|
|
|
|
void
|
|
BuildSecondaryPalette(
|
|
Palette8 *source_palette
|
|
);
|
|
|
|
void
|
|
BuildAuxiliaryPalette();
|
|
|
|
void
|
|
BuildSecondaryTranslation();
|
|
|
|
void
|
|
BuildAuxiliaryTranslation();
|
|
|
|
void
|
|
BlankPalette();
|
|
|
|
void
|
|
BuildSecondaryColor(
|
|
PaletteTriplet *source_triplet,
|
|
int color_number
|
|
);
|
|
//------------------------------------------------------------
|
|
// Protected data
|
|
//------------------------------------------------------------
|
|
int
|
|
translationTable[256];
|
|
unsigned char
|
|
myColor[256];
|
|
|
|
int
|
|
baseRotation,
|
|
bitMask,
|
|
numberOfBits,
|
|
maximumX,
|
|
maximumY;
|
|
|
|
|
|
L4GraphicsPort::ChannelEnableID
|
|
channelEnable;
|
|
};
|