gauges: all 6 cockpit MFD surfaces in a SEPARATE dev window (Milestone C)
Generalizes the single-'sec'-inset (Milestone B) into a full 6-surface compositor presented in its own top-level window -- the default under BT_DEV_GAUGES (BT_DEV_GAUGES_DOCK=1 docks it into the main window instead). A 960x384 window tiles all six pod instrument screens: Heat (COOLANT/BALANCE/ RES + condenser gauges), Comm (KILLS/DEATHS/SELECT TARGET), Mfd1/2/3 (DISPLAY/ PROGRAM/NEAREST frames), and the color radar (SCALE grid + dials + ARMOR DAMAGE). Main 800x600 3D view is un-occluded; default DEV un-regressed (TARGET DESTROYED after 8 hits, 0 crashes). Design (mapped by the mfd-multisurface-map workflow): - All 6 surfaces are bit-plane MASKS over the ONE shared SVGA16/pixelBuffer (sec=palette low byte; Heat=0x4000 UL, Mfd2=0x0400 UC, Comm=0x8000 UR, Mfd1=0x0100 LL, Mfd3=0x1000 LR), so the compositor reaches the SVGA16 once and extracts each by mask. No port-name reconcile needed on the dev path -- fetch the BT names directly; the RP aux* names only matter to the pod's own SVGA16::Update demux (a deferred pod-only fallback). - SVGA16::DrawDevSurface: two kernels -- palette-LUT (sec) + mono bit-plane-> tint ((word&mask)?tint:0). BTDrawGaugeSurfaces iterates a 6-entry table. - Separate window = one CreateAdditionalSwapChain on the existing device (no 2nd D3D device). BTGaugeWindowRenderAndPresent (after the main EndScene): SetRenderTarget -> SetDepthStencilSurface(NULL) -> Clear -> BeginScene -> 6 tiles -> EndScene -> restore -> swap->Present. - KEY BUG fixed: the main 800x600 depth surface stays bound when rendering to the 960x384 gauge backbuffer; a bound depth smaller than the RT is invalid -> all draws silently fail (clear color, no geometry). Unbind depth (Z is off), restore after. Pod SVGA16::Update/BuildWindows path byte-unchanged; all gated BT_DEV_GAUGES. Content is still the authored cockpit frames + base-table gauges -- the live MFD widgets need the BTL4MethodDescription reconstruction (this window is now the live viewer for that work). Details: docs/GAUGE_COMPOSITE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a256813395
commit
b526e92cad
@@ -675,6 +675,27 @@ method table (BT gauge classes: `PlayerStatus` + ~16 recovered `methodDescriptio
|
||||
game-state DATA BINDINGS (why some gauges resolve NULL) — that is what the dev skip/guards stand in for, and
|
||||
what the POD needs. Then Step 2 (RP↔BT MFD port-name reconcile → the 5 MFDs) + Step 3 (2-window layout).
|
||||
|
||||
**✅✅ GAUGE MILESTONE C — ALL SIX instrument surfaces in a SEPARATE dev window (default under
|
||||
`BT_DEV_GAUGES`; `BT_DEV_GAUGES_DOCK=1` = docked-in-main-window instead; full map: `docs/GAUGE_COMPOSITE.md`).**
|
||||
Steps 2+3 done for the dev path. A 960×384 top-level window tiles all 6 pod screens with authentic content:
|
||||
**Heat** (COOLANT/BALANCE/RES + condenser/temp-leak gauges), **Comm** (KILLS/DEATHS/SELECT TARGET), **Mfd1/2/3**
|
||||
(DISPLAY/PROGRAM/NEAREST/TEMP-STATUS frames), and the color **radar** (SCALE grid + dials + ARMOR DAMAGE). Main
|
||||
800×600 3D view is un-occluded; default DEV un-regressed (TARGET DESTROYED, 0 crashes). **Architecture (from the
|
||||
`mfd-multisurface-map` workflow):** all 6 surfaces are bit-plane MASKS over the ONE shared `SVGA16`/`pixelBuffer`
|
||||
(`sec`=palette low byte; `Heat`=0x4000 UL, `Mfd2`=0x0400 UC, `Comm`=0x8000 UR, `Mfd1`=0x0100 LL, `Mfd3`=0x1000 LR;
|
||||
`Eng1-3` = engineering-mode alt planes, not extra monitors), so the compositor reaches the SVGA16 ONCE and
|
||||
extracts each by mask. **No port-name reconcile needed on the dev path** — we fetch the BT port names directly via
|
||||
`GetGraphicsPort` (the RP `auxUL2/…` names only matter to the POD's own `SVGA16::Update` demux — a deferred
|
||||
pod-only fallback). `SVGA16::DrawDevSurface` has two kernels: palette-LUT (sec, == Update case 0) + mono bit-plane→
|
||||
tint (`(word&mask)?tint:0`, the reduced core of Update cases 1/2); `BTDrawGaugeSurfaces` iterates a 6-entry table.
|
||||
The **separate window = one `CreateAdditionalSwapChain` on the EXISTING device** (no 2nd D3D device); rendered
|
||||
AFTER the main `EndScene` (`BTGaugeWindowRenderAndPresent`, L4VIDEO): SetRenderTarget→**SetDepthStencilSurface(NULL)**
|
||||
→Clear→BeginScene→6 tiles→EndScene→restore→`swap->Present`. **⚠ KEY BUG (cost a cycle): depth-stencil size
|
||||
mismatch** — the main 800×600 depth stays bound when you SetRenderTarget to the 960×384 gauge backbuffer; a bound
|
||||
depth SMALLER than the RT is INVALID → all draws silently fail (window shows the Clear color, no geometry). Fix:
|
||||
unbind depth (Z is off anyway), restore after. Pod `BuildWindows`/`Update` path byte-unchanged. REMAINING = the
|
||||
widget recon (above) + the pod MFD port-name fallback.
|
||||
|
||||
**Alternate route considered:** run original `btrel410.exe` under DOSBox — **blocked**: it needs the
|
||||
VPX/IG board DOSBox can't emulate; bypassing it = the shim work anyway.
|
||||
|
||||
|
||||
+47
-6
@@ -172,10 +172,51 @@ accommodations (skip/guard) stand in for. Files touched: `L4VB16.{h,cpp}` (Draw
|
||||
`L4VIDEO.cpp` (the EndScene hook), `btl4app.{cpp,hpp}` (the override fix), `GAUGREND.cpp` (tolerant skip),
|
||||
`gauge.h` + `GAUGE.{h,cpp}` (NULL-source + SEH guards).
|
||||
|
||||
## Follow-ups (after Milestone B)
|
||||
## Milestone C DONE (2026-07) — ALL SIX instrument surfaces in a SEPARATE dev window
|
||||
|
||||
- **Widget recon (the real fix):** reconstruct `BTL4MethodDescription` (BT gauge classes) + the gauge→mech
|
||||
data bindings so the skipped/guarded gauges show real data and the POD path (strict `Fail`, no guards) works.
|
||||
- **Step 2** — reconcile the MFD port names (`auxUL2/auxC/...` in `SVGA16::Update` vs `Heat/Comm/Mfd1-3` in
|
||||
`L4GAUGE.CFG`) → the 5 MFD surfaces render (only `sec`/radar works today).
|
||||
- **Step 3** — the 2-window layout (3D-view window + an instrument-panel window tiling radar + the 5 MFDs).
|
||||
The beachhead's single-`sec`-inset is now a full **6-surface compositor in its own top-level window** (the
|
||||
default under `BT_DEV_GAUGES`; `BT_DEV_GAUGES_DOCK=1` docks the panel into the main window instead). The
|
||||
960×384 gauge window tiles all six pod instrument screens with authentic content: **Heat** (COOLANT/BALANCE/
|
||||
RES + condenser & temp-leak gauges), **Comm** (KILLS/DEATHS/SELECT TARGET scoreboard), **Mfd1/Mfd2/Mfd3**
|
||||
(DISPLAY/PROGRAM/NEAREST/TEMP-STATUS frames), and the color **radar** (SCALE grid + speed/heading dials +
|
||||
ARMOR DAMAGE schematic). Verified live: gauge window renders all 6; main 800×600 3D view is un-occluded;
|
||||
default DEV (no gauges) un-regressed (TARGET DESTROYED after 8 hits, 0 crashes).
|
||||
|
||||
**Architecture (mapped by the `mfd-multisurface-map` workflow, then implemented):**
|
||||
- **One buffer, six bit-plane views.** All gauge ports share ONE `SVGA16`/`pixelBuffer`; a "surface" is a
|
||||
MASK over that one 640×480×16 buffer. The compositor reaches the `SVGA16` once (via any port) and extracts
|
||||
each surface by its own plane mask. BT plane map (`L4GAUGE.CFG` MechInit @4395): `sec`=radar (palette,
|
||||
low byte) · `Heat`=UL (0x4000) · `Mfd2`=UC (0x0400) · `Comm`=UR (0x8000) · `Mfd1`=LL (0x0100) ·
|
||||
`Mfd3`=LR (0x1000). `Eng1/2/3` are the engineering-mode alt planes of UC/LL/LR, not extra monitors.
|
||||
- **No port-name reconcile needed on the dev path.** The RP names `auxUL2/auxC/…` hardcoded in
|
||||
`SVGA16::Update` only matter to the POD demux (a deferred pod-only follow-up); our compositor fetches the
|
||||
BT names (`Heat/Comm/Mfd1/Mfd2/Mfd3`) directly via `GetGraphicsPort` (`stricmp`).
|
||||
- **Two extract kernels** in `SVGA16::DrawDevSurface`: palette-LUT (sec/radar, == `Update` case 0) and mono
|
||||
bit-plane → tint (`(word & mask) ? tint : 0`, the reduced core of `Update` cases 1/2; MFD masks are single
|
||||
bits so no DWORD SIMD/pack). `BTDrawGaugeSurfaces` iterates a `{name,tint,cellRect}` table over all 6.
|
||||
- **Separate window = one ADDITIONAL SWAP CHAIN on the existing device** (no 2nd D3D device — textures/verts
|
||||
are shared). `BTGaugeWindowRenderAndPresent` (called AFTER the main `EndScene`, before the main Present):
|
||||
`GetBackBuffer` → `SetRenderTarget` → **`SetDepthStencilSurface(NULL)`** → Clear → BeginScene → 6 tiles →
|
||||
EndScene → restore RT+DS → `swap->Present`. The pod's own per-surface `BuildWindows`/`Update` path is
|
||||
byte-unchanged (dev mode already NULLs those objects).
|
||||
- **⚠ THE KEY BUG (cost a cycle): depth-stencil size mismatch.** The main 800×600 depth surface stays bound
|
||||
when you `SetRenderTarget` to the 960×384 gauge backbuffer; a bound depth surface SMALLER than the render
|
||||
target is INVALID → every draw silently fails (window showed the Clear color but no tiles). Fix:
|
||||
`SetDepthStencilSurface(NULL)` before the gauge draws (Z is disabled anyway), restore after. Symptom to
|
||||
remember: render-to-a-second-swap-chain shows the clear color but no geometry ⇒ check the bound depth size.
|
||||
|
||||
**Content reality (unchanged from Milestone B):** the surfaces show the authored cockpit FRAME art + a few
|
||||
base-table gauges (heat numerics, radar) — the live animated MFD widgets are BT-specific gauge classes not
|
||||
yet reconstructed (parse-skipped). The separate window is now the **live viewer** for that widget-recon work.
|
||||
|
||||
## Follow-ups (after Milestone C)
|
||||
|
||||
- **Widget recon (the real fix, the big remaining workstream):** populate `BTL4MethodDescription` (give each
|
||||
BT gauge class a `methodDescription`) + wire the gauge→game-state `Execute()` data bindings, so the
|
||||
skipped/guarded widgets (`map`/`GeneratorCluster`/`vehicleSubSystems`/`pilotList`/`cmArmor`/heat clusters)
|
||||
render live data. The Milestone-C window shows them coming online as they're reconstructed.
|
||||
- **Pod MFD port-name reconcile (pod-only):** a ~5-line positional dual-name fallback in `SVGA16::Update`
|
||||
(`UL = GetGraphicsPort("auxUL2"); if(!UL) UL = GetGraphicsPort("Heat"); …`) so BT MFDs reach the pod's real
|
||||
monitors. NOT a CFG rename (the CFG uses BT names pervasively + `MUNGA_L4` is shared with RP's aux names).
|
||||
- **Polish:** overlay-plane compositing over `sec`; a pod-accurate RGB-packing toggle; texture atlas
|
||||
(one lock/upload); device-reset recreate hardening for the additional swap chain.
|
||||
|
||||
+248
-36
@@ -109,6 +109,15 @@ static bool DevGaugeComposite()
|
||||
return v != 0;
|
||||
}
|
||||
|
||||
// DEV-COMPOSITE: BT_DEV_GAUGES_DOCK=1 docks the 6-surface panel INTO the main 800x600
|
||||
// window (occludes the 3D view); default (unset) presents them in a SEPARATE window.
|
||||
static bool DevGaugeDocked()
|
||||
{
|
||||
static int v = -1;
|
||||
if (v < 0) v = (getenv("BT_DEV_GAUGES_DOCK") != NULL) ? 1 : 0;
|
||||
return v != 0;
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
// DEV-COMPOSITE (option B) -- draw the woken gauge renderer's SECONDARY/radar
|
||||
// surface as an inset in the MAIN window. The gauge widgets have already
|
||||
@@ -117,50 +126,70 @@ static bool DevGaugeComposite()
|
||||
// a quad. Called by the main renderer as the LAST draw before EndScene, so no
|
||||
// render-state save/restore is needed (the next frame resets device state).
|
||||
//===========================================================================//
|
||||
void SVGA16::DrawDevInset(LPDIRECT3DDEVICE9 device, int secMask, int paletteID)
|
||||
void SVGA16::DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int paletteID,
|
||||
int monoTint, float dstX, float dstY, float dstW, float dstH)
|
||||
{
|
||||
int w = pixelBuffer.Data.Size.x; // 640
|
||||
int h = pixelBuffer.Data.Size.y; // 480
|
||||
if (device == NULL || w <= 0 || h <= 0) return;
|
||||
if (device == NULL || slot < 0 || slot >= 8 || w <= 0 || h <= 0) return;
|
||||
|
||||
// Lazily create the inset texture on the MAIN device (MANAGED -> lockable +
|
||||
// Lazily create this surface's texture on the MAIN device (MANAGED -> lockable +
|
||||
// survives device reset).
|
||||
if (mDevInsetTex == NULL)
|
||||
device->CreateTexture(w, h, 1, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &mDevInsetTex, NULL);
|
||||
if (mDevInsetTex == NULL) return;
|
||||
if (mDevSurfaceTex[slot] == NULL)
|
||||
device->CreateTexture(w, h, 1, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &mDevSurfaceTex[slot], NULL);
|
||||
LPDIRECT3DTEXTURE9 tex = mDevSurfaceTex[slot];
|
||||
if (tex == NULL) return;
|
||||
|
||||
// Palette-expand the secondary plane into the texture (== SVGA16::Update case 0).
|
||||
SVGA16Palette *pal = &palette[paletteID];
|
||||
// Extract this surface's bit-plane from the ONE shared pixelBuffer into the texture.
|
||||
D3DLOCKED_RECT rect;
|
||||
if (SUCCEEDED(mDevInsetTex->LockRect(0, &rect, NULL, 0)))
|
||||
if (SUCCEEDED(tex->LockRect(0, &rect, NULL, 0)))
|
||||
{
|
||||
Word *source = pixelBuffer.Data.MapPointer;
|
||||
Word *dest = (Word*)rect.pBits;
|
||||
int postRowIncrement = (rect.Pitch / 2) - w;
|
||||
for (int y = 0; y < h; y++)
|
||||
if (monoTint < 0)
|
||||
{
|
||||
for (int x = 0; x < w; x++)
|
||||
// PALETTE surface (sec/radar) -- palette-LUT expand (== SVGA16::Update case 0).
|
||||
SVGA16Palette *pal = &palette[paletteID];
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
PaletteTriplet *pe = &(pal->paletteData.Color[*source & secMask]);
|
||||
*dest = ((pe->Red >> 3) << 11) | ((pe->Green >> 2) << 5) | (pe->Blue >> 3);
|
||||
dest++; source++;
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
PaletteTriplet *pe = &(pal->paletteData.Color[*source & mask]);
|
||||
*dest = ((pe->Red >> 3) << 11) | ((pe->Green >> 2) << 5) | (pe->Blue >> 3);
|
||||
dest++; source++;
|
||||
}
|
||||
dest += postRowIncrement;
|
||||
}
|
||||
dest += postRowIncrement;
|
||||
}
|
||||
mDevInsetTex->UnlockRect(0);
|
||||
else
|
||||
{
|
||||
// MONO MFD surface -- single-bit-plane expand to a tint (the reduced core of
|
||||
// SVGA16::Update cases 1/2; MFD masks are single bits, so no DWORD SIMD/pack).
|
||||
Word tint = (Word) monoTint;
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
*dest = (*source & mask) ? tint : 0;
|
||||
dest++; source++;
|
||||
}
|
||||
dest += postRowIncrement;
|
||||
}
|
||||
}
|
||||
tex->UnlockRect(0);
|
||||
}
|
||||
|
||||
// Draw the inset: a 320x240 panel in the bottom-left of the (800x600) backbuffer.
|
||||
const float iw = 320.0f, ih = 240.0f, ix = 8.0f, iy = 600.0f - ih - 8.0f;
|
||||
// Draw the surface quad at (dstX,dstY,dstW,dstH) in the current render target.
|
||||
struct InsetVert { float x, y, z, rhw, u, v; };
|
||||
InsetVert quad[4] =
|
||||
{
|
||||
{ ix, iy, 0.0f, 1.0f, 0.0f, 0.0f },
|
||||
{ ix + iw, iy, 0.0f, 1.0f, 1.0f, 0.0f },
|
||||
{ ix + iw, iy + ih, 0.0f, 1.0f, 1.0f, 1.0f },
|
||||
{ ix, iy + ih, 0.0f, 1.0f, 0.0f, 1.0f },
|
||||
{ dstX, dstY, 0.0f, 1.0f, 0.0f, 0.0f },
|
||||
{ dstX + dstW, dstY, 0.0f, 1.0f, 1.0f, 0.0f },
|
||||
{ dstX + dstW, dstY + dstH, 0.0f, 1.0f, 1.0f, 1.0f },
|
||||
{ dstX, dstY + dstH, 0.0f, 1.0f, 0.0f, 1.0f },
|
||||
};
|
||||
device->SetTexture(0, mDevInsetTex);
|
||||
device->SetTexture(0, tex);
|
||||
device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
|
||||
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
|
||||
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
@@ -173,23 +202,205 @@ void SVGA16::DrawDevInset(LPDIRECT3DDEVICE9 device, int secMask, int paletteID)
|
||||
device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, sizeof(InsetVert));
|
||||
}
|
||||
|
||||
//
|
||||
// The pod's SIX instrument surfaces, all bit-plane views of the ONE shared 640x480
|
||||
// gauge buffer (masks/positions from content/GAUGE/L4GAUGE.CFG MechInit @4395):
|
||||
// sec = radar/secondary (palette) Heat = UL Mfd2 = UC Comm = UR
|
||||
// Mfd1 = LL Mfd3 = LR
|
||||
// monoTint < 0 -> palette-expand; else the mono tint for that MFD plane. The MFD
|
||||
// content today is mostly the authored cockpit frame art (btquad/btcomm/btheat) --
|
||||
// the live widgets are BT-specific gauge classes not yet reconstructed (parse-skipped).
|
||||
//
|
||||
struct BTGaugeSurfaceDesc
|
||||
{
|
||||
const char *portName;
|
||||
int monoTint; // -1 = palette (sec); else R5G6B5 tint
|
||||
float cellX, cellY; // normalized cell origin within the panel (y down)
|
||||
float cellW, cellH; // normalized cell size
|
||||
};
|
||||
|
||||
static const BTGaugeSurfaceDesc kBTGaugeSurfaces[6] =
|
||||
{
|
||||
// top row: Heat (UL) | Mfd2 (UC) | Comm (UR)
|
||||
{ "Heat", 0xFFFF, 0.0f / 3, 0.0f, 1.0f / 3, 0.5f },
|
||||
{ "Mfd2", 0xFFFF, 1.0f / 3, 0.0f, 1.0f / 3, 0.5f },
|
||||
{ "Comm", 0xFFFF, 2.0f / 3, 0.0f, 1.0f / 3, 0.5f },
|
||||
// bottom row: Mfd1 (LL) | sec/radar (center) | Mfd3 (LR)
|
||||
{ "Mfd1", 0xFFFF, 0.0f / 3, 0.5f, 1.0f / 3, 0.5f },
|
||||
{ "sec", -1, 1.0f / 3, 0.5f, 1.0f / 3, 0.5f },
|
||||
{ "Mfd3", 0xFFFF, 2.0f / 3, 0.5f, 1.0f / 3, 0.5f },
|
||||
};
|
||||
|
||||
//
|
||||
// Composite ALL SIX gauge surfaces into the CURRENT render target, tiled within the
|
||||
// panel rect (px,py,pw,ph). Reaches the ONE shared SVGA16 via any resolvable port and
|
||||
// extracts each surface by its own bit-plane mask. Ports that don't resolve are skipped.
|
||||
//
|
||||
void BTDrawGaugeSurfaces(LPDIRECT3DDEVICE9 device, float px, float py, float pw, float ph)
|
||||
{
|
||||
if (device == NULL) return;
|
||||
GaugeRenderer *gr = application ? application->GetGaugeRenderer() : NULL;
|
||||
if (gr == NULL) return;
|
||||
|
||||
// Reach the shared display once (any present port shares the same SVGA16/pixelBuffer).
|
||||
SVGA16 *svga = NULL;
|
||||
for (int i = 0; i < 6 && svga == NULL; i++)
|
||||
{
|
||||
L4GraphicsPort *p = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(kBTGaugeSurfaces[i].portName));
|
||||
if (p != NULL) svga = (SVGA16*) p->graphicsDisplay;
|
||||
}
|
||||
if (svga == NULL) return;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
const BTGaugeSurfaceDesc &d = kBTGaugeSurfaces[i];
|
||||
L4GraphicsPort *port = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(d.portName));
|
||||
if (port == NULL) continue; // this surface's port isn't configured -> skip
|
||||
svga->DrawDevSurface(
|
||||
device, i, port->GetBitMask(), port->paletteID, d.monoTint,
|
||||
px + d.cellX * pw, py + d.cellY * ph, d.cellW * pw, d.cellH * ph);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Free entry point the MAIN renderer calls (L4VIDEO DPLRenderer::ExecuteImplementation,
|
||||
// just before EndScene). No-op unless BT_DEV_GAUGES is set + the gauge renderer + its
|
||||
// 'sec' port exist. Reaches the gauge renderer's SVGA16 and blits the secondary inset.
|
||||
// just before EndScene). No-op unless BT_DEV_GAUGES is set. Docks the 6-surface panel
|
||||
// in the bottom-left of the 800x600 main window (a second dedicated window is the goal;
|
||||
// see BTGaugeWindow* below).
|
||||
//
|
||||
void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device)
|
||||
{
|
||||
if (!DevGaugeComposite() || device == NULL) return;
|
||||
GaugeRenderer *gr = application ? application->GetGaugeRenderer() : NULL;
|
||||
if (gr == NULL) return;
|
||||
// The secondary/radar surface ('sec') is the one port that resolves without
|
||||
// the RP<->BT MFD port-name reconciliation (Step 2), so it is the beachhead.
|
||||
L4GraphicsPort *secPort = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort("sec"));
|
||||
if (secPort == NULL) return;
|
||||
SVGA16 *svga = (SVGA16*)secPort->graphicsDisplay;
|
||||
if (svga == NULL) return;
|
||||
svga->DrawDevInset(device, secPort->GetBitMask(), secPort->paletteID);
|
||||
if (!DevGaugeComposite() || !DevGaugeDocked() || device == NULL) return;
|
||||
// DOCKED mode only: draw into the main backbuffer (this runs before EndScene).
|
||||
BTDrawGaugeSurfaces(device, 2.0f, 300.0f, 540.0f, 296.0f);
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
// DEV-COMPOSITE -- the SEPARATE cockpit-MFD window (the default under BT_DEV_GAUGES).
|
||||
// A second top-level window fed by an ADDITIONAL SWAP CHAIN on the existing main
|
||||
// device (no second D3D device needed -- textures/vertex data are shared). The pod's
|
||||
// own per-surface fullscreen-device path (SVGA16::BuildWindows) is untouched; this is
|
||||
// the dev-box analog of the pod's separate instrument monitors, collapsed into one
|
||||
// tiled window. BTGaugeWindowRenderAndPresent is called AFTER the main EndScene.
|
||||
//===========================================================================//
|
||||
static HWND s_gaugeHwnd = NULL;
|
||||
static IDirect3DSwapChain9 *s_gaugeSwap = NULL;
|
||||
static const int s_gaugeW = 960;
|
||||
static const int s_gaugeH = 384;
|
||||
|
||||
static LRESULT CALLBACK BTGaugeWndProc(HWND h, UINT m, WPARAM w, LPARAM l)
|
||||
{
|
||||
if (m == WM_CLOSE) { ShowWindow(h, SW_HIDE); return 0; } // hide, never quit the app
|
||||
return DefWindowProc(h, m, w, l);
|
||||
}
|
||||
|
||||
// Lazily create the window + its additional swap chain (on the MAIN device).
|
||||
static bool BTGaugeWindowEnsure(LPDIRECT3DDEVICE9 device)
|
||||
{
|
||||
if (s_gaugeSwap != NULL) return true;
|
||||
if (device == NULL) return false;
|
||||
|
||||
HINSTANCE hInst = GetModuleHandle(NULL);
|
||||
static bool s_classReg = false;
|
||||
if (!s_classReg)
|
||||
{
|
||||
WNDCLASSA wc;
|
||||
memset(&wc, 0, sizeof(wc));
|
||||
wc.lpfnWndProc = BTGaugeWndProc;
|
||||
wc.hInstance = hInst;
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
||||
wc.lpszClassName = "BTGaugeWnd";
|
||||
RegisterClassA(&wc);
|
||||
s_classReg = true;
|
||||
}
|
||||
if (s_gaugeHwnd == NULL)
|
||||
{
|
||||
RECT r = { 0, 0, s_gaugeW, s_gaugeH };
|
||||
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE);
|
||||
s_gaugeHwnd = CreateWindowExA(
|
||||
0, "BTGaugeWnd", "BattleTech - Cockpit MFDs", WS_OVERLAPPEDWINDOW,
|
||||
40, 40, r.right - r.left, r.bottom - r.top, NULL, NULL, hInst, NULL);
|
||||
if (s_gaugeHwnd == NULL) return false;
|
||||
ShowWindow(s_gaugeHwnd, SW_SHOWNOACTIVATE);
|
||||
}
|
||||
|
||||
// Match the main swap chain's backbuffer format so the additional chain is compatible.
|
||||
D3DFORMAT bbFormat = D3DFMT_X8R8G8B8;
|
||||
IDirect3DSwapChain9 *mainSwap = NULL;
|
||||
if (SUCCEEDED(device->GetSwapChain(0, &mainSwap)) && mainSwap != NULL)
|
||||
{
|
||||
D3DPRESENT_PARAMETERS mpp;
|
||||
if (SUCCEEDED(mainSwap->GetPresentParameters(&mpp)))
|
||||
bbFormat = mpp.BackBufferFormat;
|
||||
mainSwap->Release();
|
||||
}
|
||||
|
||||
D3DPRESENT_PARAMETERS pp;
|
||||
memset(&pp, 0, sizeof(pp));
|
||||
pp.Windowed = TRUE;
|
||||
pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||
pp.hDeviceWindow = s_gaugeHwnd;
|
||||
pp.BackBufferFormat = bbFormat;
|
||||
pp.BackBufferWidth = s_gaugeW;
|
||||
pp.BackBufferHeight = s_gaugeH;
|
||||
pp.BackBufferCount = 1;
|
||||
if (FAILED(device->CreateAdditionalSwapChain(&pp, &s_gaugeSwap)))
|
||||
{
|
||||
s_gaugeSwap = NULL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Render the 6 surfaces into the gauge window's swap chain and present it. Called AFTER
|
||||
// the main EndScene, BEFORE the main Present (so it can BeginScene/EndScene on its own).
|
||||
void BTGaugeWindowRenderAndPresent(LPDIRECT3DDEVICE9 device)
|
||||
{
|
||||
if (!DevGaugeComposite() || DevGaugeDocked() || device == NULL) return;
|
||||
if (!BTGaugeWindowEnsure(device)) return;
|
||||
|
||||
IDirect3DSurface9 *gaugeBB = NULL;
|
||||
if (FAILED(s_gaugeSwap->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &gaugeBB)) || gaugeBB == NULL)
|
||||
{
|
||||
// device likely lost -> drop the chain so it recreates next frame
|
||||
s_gaugeSwap->Release();
|
||||
s_gaugeSwap = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
IDirect3DSurface9 *mainRT = NULL;
|
||||
device->GetRenderTarget(0, &mainRT);
|
||||
IDirect3DSurface9 *mainDS = NULL;
|
||||
device->GetDepthStencilSurface(&mainDS); // may be NULL
|
||||
|
||||
device->SetRenderTarget(0, gaugeBB);
|
||||
// Unbind the main depth-stencil: it is 800x600 but the gauge backbuffer is 960x384
|
||||
// (wider) -- a bound depth surface smaller than the render target is INVALID and makes
|
||||
// every draw silently fail. We don't need depth here (Z is disabled per surface quad).
|
||||
device->SetDepthStencilSurface(NULL);
|
||||
device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(12, 12, 16), 1.0f, 0);
|
||||
if (SUCCEEDED(device->BeginScene()))
|
||||
{
|
||||
BTDrawGaugeSurfaces(device, 0.0f, 0.0f, (float) s_gaugeW, (float) s_gaugeH);
|
||||
device->EndScene();
|
||||
}
|
||||
|
||||
if (mainRT != NULL) // restore the main render target + depth for the next frame
|
||||
{
|
||||
device->SetRenderTarget(0, mainRT);
|
||||
mainRT->Release();
|
||||
}
|
||||
device->SetDepthStencilSurface(mainDS); // NULL is valid (restores "no depth")
|
||||
if (mainDS != NULL)
|
||||
mainDS->Release();
|
||||
gaugeBB->Release();
|
||||
|
||||
if (s_gaugeSwap->Present(NULL, NULL, s_gaugeHwnd, NULL, 0) == D3DERR_DEVICELOST)
|
||||
{
|
||||
s_gaugeSwap->Release();
|
||||
s_gaugeSwap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed, int *secondaryIndex, int *aux1Index, int *aux2Index)
|
||||
@@ -3927,7 +4138,8 @@ SVGA16::SVGA16(
|
||||
):Video16BitBuffered(init_width, init_height)
|
||||
{
|
||||
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
|
||||
mDevInsetTex = NULL; // DEV-COMPOSITE (option B): lazily created on first inset draw
|
||||
for (int _i = 0; _i < 8; _i++) // DEV-COMPOSITE: lazily created on first surface draw
|
||||
mDevSurfaceTex[_i] = NULL;
|
||||
//STUBBED: VIDEO RB 1/15/07
|
||||
# if defined(DEBUG)
|
||||
Tell("SVGA16::SVGA16()\n");
|
||||
|
||||
@@ -292,12 +292,16 @@ private:
|
||||
|
||||
int mDisplayToUpdate;
|
||||
|
||||
LPDIRECT3DTEXTURE9 mDevInsetTex; // DEV-COMPOSITE (option B): the secondary/radar
|
||||
// plane uploaded to a texture on the MAIN device.
|
||||
LPDIRECT3DTEXTURE9 mDevSurfaceTex[8]; // DEV-COMPOSITE: one texture per gauge surface
|
||||
// (sec/radar + the 5 MFD bit-planes), on the MAIN device.
|
||||
public:
|
||||
// DEV-COMPOSITE (option B): palette-expand the secondary plane into mDevInsetTex on
|
||||
// the given (main) device + draw it as an inset quad. See BTDrawGaugeInset (L4VB16.cpp).
|
||||
void DrawDevInset(LPDIRECT3DDEVICE9 device, int secMask, int paletteID);
|
||||
// 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).
|
||||
void DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int paletteID,
|
||||
int monoTint, float dstX, float dstY, float dstW, float dstH);
|
||||
};
|
||||
|
||||
//########################################################################
|
||||
|
||||
@@ -6963,13 +6963,19 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
|
||||
if (mCamShipHUD)
|
||||
mCamShipHUD->Render(0, &viewTransform);
|
||||
|
||||
// DEV-COMPOSITE (option B): blit the woken gauge renderer's secondary/radar surface
|
||||
// as an inset in this window (LAST draw before EndScene; no-op unless BT_DEV_GAUGES).
|
||||
// DEV-COMPOSITE: in DOCKED mode (BT_DEV_GAUGES_DOCK) blit the 6-surface gauge panel
|
||||
// into this window as the LAST draw before EndScene (no-op otherwise / off pod).
|
||||
extern void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device);
|
||||
BTDrawGaugeInset(mDevice);
|
||||
|
||||
hr = mDevice->EndScene();
|
||||
|
||||
// DEV-COMPOSITE: default mode -- render the 6 cockpit surfaces into a SEPARATE window
|
||||
// (its own additional swap chain on this device) + present it, between the main
|
||||
// EndScene and the main Present. No-op unless BT_DEV_GAUGES (and not docked mode).
|
||||
extern void BTGaugeWindowRenderAndPresent(LPDIRECT3DDEVICE9 device);
|
||||
BTGaugeWindowRenderAndPresent(mDevice);
|
||||
|
||||
// DIAG (turn-hitch hunt): draw CPU is _rt0..here; Present blocks on the GPU.
|
||||
LARGE_INTEGER _rt1; QueryPerformanceCounter(&_rt1);
|
||||
hr = mDevice->Present(NULL, NULL, NULL, NULL);
|
||||
|
||||
Reference in New Issue
Block a user