platform profile + gauge dev-composite (Milestone A: gauge renderer woken)
PLATFORM PROFILE (-platform pod|dev / BT_PLATFORM / run.cmd pod; default dev): an env-preset selector, NOT a code fork -- it picks which environment the existing video/gauge/input code reads. dev = single 800x600 window + keyboard (the working build); pod = RIO input + (on real hardware) the multi-surface gauges/MFDs, mirroring content/SETENV.BAT (the pod path -- FindBestAdapterIndices / SVGA16 -- is left untouched). The multi-surface needs the pod's 2 video cards, so -platform pod does NOT auto-enable L4GAUGE on a dev box (stays bootable, single-window). Also fixes a real engine bug: SVGA16::BuildWindows PostQuitMessage'd on a CreateDevice failure but fell through to a null-device deref (segfault) -- now breaks (inert on the pod). GAUGE DEV-COMPOSITE, Milestone A (option B; opt-in BT_DEV_GAUGES, default OFF): wake the (dormant) gauge renderer so its CPU-rastered pixelBuffer can later be composited into the dev window, WITHOUT touching the pod SVGA16 output path. SVGA16 does no per-surface D3D in this mode (a DevGaugeComposite() gate forces the no-surface path + short-circuits Update/Refresh). Waking the never-exercised gauge subsystem exposed 4 latent reconstruction bugs, each guarded: SVGA16::Update / Refresh (empty mSurfaces[]), LBE4ControlsManager::MakeLinkedLamp (NULL lamp manager), L4GaugeRenderer::NotifyOfNewInterestingEntity (garbage warehouse chain). It now boots STABLY; the gauge reconstruction is incomplete (shadowed lamp-manager + warehouse members) -- Milestone B (the composite pass) will reveal whether the content is real. See docs/GAUGE_COMPOSITE.md. Verified: default DEV un-regressed (combat DESTROYED, 0 crashes); pod path untouched; BT_DEV_GAUGES boots stable. 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
8b36440d05
commit
c13e72d0ba
@@ -619,6 +619,32 @@ Old roadmap below is superseded for Phases 1–3 (engine/renderer/HAL/audio = do
|
||||
- **Phase 8 — Pod bring-up (with Nick):** 7-monitor layout, old-driver compatibility, RIO cockpit I/O
|
||||
wiring, on-pod testing.
|
||||
|
||||
**✅ PLATFORM PROFILE scaffold DONE (`-platform pod|dev`, or `BT_PLATFORM` env, or `run\run.cmd [EGG] pod`;
|
||||
default DEV).** The gate before Phase-4 HUD/MFD work: a boot switch selecting a runtime profile WITHOUT forking
|
||||
the codepath — it just picks WHICH environment preset the existing video/gauge/input code reads (btl4main.cpp
|
||||
env-defaults block). **KEY FINDING (a workflow map): the pod multi-surface path already EXISTS and is intact** —
|
||||
`DPLRenderer::FindBestAdapterIndices` (multi-adapter/monitor selector; honors PRIMGAUGE/SECGAUGE/MFDGAUGE/
|
||||
SPANDISABLE), `SVGA16::BuildWindows` (L4VB16.cpp — a fullscreen D3D device + window per gauge/MFD surface, MFD
|
||||
span = width×2), `L4GaugeRenderer` (gated on `getenv("L4GAUGE")`, btl4app.cpp:353), and **`content/SETENV.BAT`
|
||||
IS the authentic pod env preset** (L4CONTROLS=RIO,KEYBOARD; L4GAUGE=640x480x16; L4PLASMA=com2). So the profile
|
||||
is an env-preset selector: **DEV (default)** = keyboard + single 800×600 window (no L4GAUGE → MakeGaugeRenderer
|
||||
NULL → single surface); **POD** = RIO cockpit input (→ keyboard fallback when no serial) + the multi-surface
|
||||
gauges/MFDs. Verified: dev un-regressed (boots/ticks/combat DESTROYED); POD boots on a dev box single-window,
|
||||
0 crashes; both the CLI (`-platform pod` — ParseCommandLine tolerates it) and env (`BT_PLATFORM=pod`) forms work.
|
||||
⚠ The multi-surface is a POD-HARDWARE capability (each surface needs its OWN fullscreen device on one of the
|
||||
pod's 2 video CARDS — monitor count alone is NOT a valid proxy), so `-platform pod` does NOT auto-enable L4GAUGE:
|
||||
it stays bootable everywhere as single-window + pod input; on the real pod the gauges come from SETENV.BAT (or an
|
||||
explicit L4GAUGE), running the UNTOUCHED FindBestAdapterIndices/SVGA16 path. Also fixed a real engine bug
|
||||
(L4VB16.cpp:201): `SVGA16::BuildWindows` PostQuitMessage'd on `CreateDevice` failure but fell through to a null
|
||||
`mDevice[j]` deref (segfault) → now `break`s (inert on pod where CreateDevice succeeds). **FOLLOW-UPS** (the deep
|
||||
work this scaffold gates): (1) **MFD compositing on a dev box** — render the gauges/MFDs into the single window /
|
||||
extra windows so POD mode is VISUALLY testable off-pod (the real "dev test of the pod HUD"); (2) **graceful
|
||||
multi-surface fallback** — the gauge renderer returns NULL instead of crashing when its surfaces can't be built,
|
||||
so a forced L4GAUGE on non-pod hardware degrades cleanly; (3) **pod main-view fullscreen/adapter tuning**
|
||||
(btl4vid.cpp:863 still binds the main 3D view to 800×600 windowed on `::GetActiveWindow()` — fine for dev + the
|
||||
pod's 800×600 main monitor, but pod fullscreen-on-a-chosen-adapter is a Phase-8 detail). The Phase-4 HUD/MFD
|
||||
render work now targets the profile's LOGICAL surfaces, not a hardcoded single window.
|
||||
|
||||
**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.
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# Gauge / MFD render pipeline — map + dev-composite plan
|
||||
|
||||
**Status:** pipeline MAPPED (workflow `gauge-pipeline-map`, 2026-07); the dev composite is **planned, not
|
||||
yet built**. This is the execute-ready plan for making the pod's gauges/MFDs render + be VISUALLY testable
|
||||
on a dev box — the "MFD compositing on dev" follow-up the platform profile gates (see §8 / the PLATFORM
|
||||
PROFILE entry in CLAUDE.md). Companion: the platform-profile scaffold (`-platform pod|dev`).
|
||||
|
||||
## The draw model — CONFIRMED (the good news)
|
||||
|
||||
**CPU-raster → texture-upload, DEVICE-INDEPENDENT.** Every gauge widget (the radar included) is a *software
|
||||
rasterizer* that writes 16-bit R5G6B5 pixels into ONE shared CPU buffer (`Video16BitBuffered::pixelBuffer`,
|
||||
a `PixelMap16`, `L4VB16.h:140`). Logical surfaces are packed by **BIT-PLANE** into that one 640×480×16 buffer
|
||||
(each MFD = one bit mask; the radar/secondary = the low byte, palette-indexed). D3D is touched **ONLY** in
|
||||
`SVGA16::Update` (`L4VB16.cpp:4041`): LockRect a `D3DPOOL_SYSTEMMEM` staging texture → a CPU expand loop
|
||||
(palette-LUT for the radar, case 0; bit-plane→RGB-channel demux for the MFDs, case 1/2) → UnlockRect →
|
||||
`UpdateTexture` staging→`D3DPOOL_DEFAULT` → ONE fullscreen textured quad (`TRIANGLEFAN`) → `Present`, **per
|
||||
surface on its own per-adapter device**. So redirecting the content to a texture on the MAIN device is a
|
||||
small change: the entire raster stays byte-identical; only the upload target + the final blit move.
|
||||
|
||||
## The 3 gotchas (why it's a multi-step build, not a one-liner)
|
||||
|
||||
1. **DORMANT in BT.** `BTL4GaugeRenderer` passes `L4GaugeRenderer(false, NULL,NULL,NULL)` (`btl4grnd.cpp:151`)
|
||||
→ `NUMGAUGEWINDOWS=0`; and `MakeGaugeRenderer` only builds anything if `getenv("L4GAUGE")` (`btl4app.cpp:353`).
|
||||
Both must change to wake it. With 0 surfaces, `SVGA16::Update` may index an empty `mSurfaces[]` → guard needed.
|
||||
2. **RP↔BT PORT-NAME MISMATCH.** `SVGA16::Update` hardcodes the Red-Planet MFD port names
|
||||
`auxUL2/auxC/auxUR2/auxLL/auxLR` (`L4VB16.cpp:4056-4061`), but BT's `content/GAUGE/L4GAUGE.CFG` names them
|
||||
`Heat/Comm/Mfd1/Mfd2/Mfd3` (:4395-4410). `GetGraphicsPort` returns NULL for the RP names → the MFD branch
|
||||
early-outs ("No MFDs to draw"). **ONLY the `sec`/radar surface matches today** — the 5 MFDs render nothing
|
||||
until the names are reconciled.
|
||||
3. **CROSS-DEVICE + STATE.** Folding onto the main device means the gauge blit shares the main render state
|
||||
(save/restore around it) + an ORDERING concern: is `l4_application->GetVideoRenderer()->GetDevice()` valid
|
||||
when the gauge renderer builds during `Application::Initialize`? Verify build order or defer texture creation.
|
||||
|
||||
## Architecture options (dev)
|
||||
|
||||
- **(A) `SVGA16` dev-composite branch.** In `SVGA16::BuildWindows`/`Update`, when a dev-composite flag is set,
|
||||
use the MAIN device for the staging/default textures, skip the per-adapter device/window/`Present`; the main
|
||||
renderer composites the DEFAULT textures as insets. Reuses the expand loops verbatim; **branches the pod
|
||||
code** (guard on the flag → inert on pod).
|
||||
- **(B) Bypass `SVGA16`.** Leave the `SVGA16` pod path untouched; add a composite pass in the main renderer
|
||||
that reads the shared `pixelBuffer` + palette directly and runs the expand + inset blit itself. **Best
|
||||
protects the pod path**; re-implements the expand + needs `pixelBuffer`/palette accessors on
|
||||
`Video16BitBuffered`/`SVGA16`.
|
||||
- **RECOMMEND (B)** for the beachhead (pod path untouched); fall back to (A) if reusing the expand loops
|
||||
wholesale is preferred. Decide this with the operator before building — it's the main design fork.
|
||||
|
||||
## Beachhead (Step 1) — one surface visible on dev
|
||||
|
||||
Goal: the **secondary/radar** surface composited as an **INSET** in the existing 800×600 window (no 2nd
|
||||
window yet). It's the one surface that works WITHOUT the port-name fix, so it proves the whole chain end-to-end.
|
||||
1. **Wake** it via a dev-composite opt-in (e.g. `BT_DEV_GAUGES` env, default OFF so DEV/pod defaults are
|
||||
unaffected; or gate on `-platform pod` on a dev box). When on: set `L4GAUGE` (so `MakeGaugeRenderer` builds)
|
||||
+ set the composite flag. Guard `SVGA16::Update` for 0 surfaces (no crash).
|
||||
2. **Raster**: the widgets already raster into `pixelBuffer` (driven by `GaugeRenderer::ProcessOneActiveGauge`).
|
||||
3. **Composite**: each frame, in the main renderer's present, upload the secondary-expanded pixels to a
|
||||
main-device texture + draw an inset quad (`XYZRHW|TEX1`), main render state saved/restored. Reach the buffer
|
||||
via `GetGaugeRenderer()`.
|
||||
- **Verify**: the radar/secondary inset appears; DEV default (no `BT_DEV_GAUGES`) un-regressed; pod path
|
||||
untouched; 0 crashes / 0 heap.
|
||||
|
||||
## Follow-ups (after the beachhead)
|
||||
|
||||
- **Step 2** — reconcile the MFD port names (rename in `L4GAUGE.CFG` or in `SVGA16::Update`) → the 5 MFDs render.
|
||||
- **Step 3** — the 2-window layout (3D-view window + an instrument-panel window tiling radar + the 5 MFDs).
|
||||
- Confirm the MFD widget CONTENT is actually fed each frame (entity→gauge routing
|
||||
`BTL4GaugeRenderer::NotifyOfNewInterestingEntity` → the `L4Warehouse` gauge-image bin).
|
||||
|
||||
## Key files / hooks
|
||||
|
||||
- `engine/MUNGA_L4/L4VB16.cpp`: `SVGA16::Update:4041` (the CPU→texture→blit crux; expand loops :4124-4191),
|
||||
`SVGA16::BuildWindows:101` (per-surface device/window/texture — the pod path), `Video16BitBuffered::Draw*`
|
||||
(:761/847/1607/1839, the software raster into `pixelBuffer`), `pixelBuffer` `L4VB16.h:140`, RP port names :4056-4061.
|
||||
- `engine/MUNGA_L4/L4GREND.cpp`: `L4GaugeRenderer` ctor:57 (builds `SVGA16` from `L4GAUGE.INI`),
|
||||
`ExecuteBackgroundDisplayUpdate:369` (calls `graphicsDisplay->Update`), `BuildGraphicsPort:485` (every port
|
||||
shares ONE `pixelBuffer`, bit-plane per port).
|
||||
- `engine/MUNGA/GAUGREND.cpp`: `ExecuteForeground:3556` / `ProcessOneActiveGauge:3836` (draw-all-gauges into `pixelBuffer`).
|
||||
- `game/reconstructed/btl4grnd.cpp`: ctor:151 (the un-dormant point — NULL indices),
|
||||
`NotifyOfNewInterestingEntity:250` (routes `GaugeImageStream` type-0x12 entities to moving/static bins).
|
||||
- `game/reconstructed/btl4rdr.cpp`: `MapDisplay` (the radar; draws into the secondary surface).
|
||||
- Main device: `l4_application->GetVideoRenderer()->GetDevice()` (`L4VIDEO.h:376`).
|
||||
- Config: `content/GAUGE/L4GAUGE.CFG` (port→bitmask map, MechInit :4395-4410), `L4GAUGE.INI` (640×480×16 page).
|
||||
|
||||
## Surface → content map (for the 2-window layout)
|
||||
|
||||
- **Surface 0 = SECONDARY / radar** (640×480, palette-indexed low byte `sec` 0x003F + `overlay` 0x00C0):
|
||||
the radar `MapDisplay` + secondary instruments (message board / heading / speed arc / armor-critical-heat maps).
|
||||
- **Surface 1(+2) = the 5 MFDs** (bit-planes 0x0100–0x8000 of the shared buffer, demuxed to R/G/B channels;
|
||||
pod spans them as one 1280×480): `Mfd1`=LL, `Mfd2`=UC, `Mfd3`=LR, `Heat`=UL, `Comm`=UR (+ `Eng1-3`).
|
||||
- `Plasma` (CFG port 10, `L4PLASMA=com2`) is an EXTERNAL serial annunciator — not a D3D surface.
|
||||
|
||||
## Progress log
|
||||
|
||||
**Milestone A DONE (2026-07) — the gauge renderer is woken + boots STABLY on a dev box** (opt-in
|
||||
`BT_DEV_GAUGES`, default OFF; default DEV + pod paths verified un-regressed — DEV combat DESTROYED,
|
||||
0 crashes). Option **B** chosen. `BT_DEV_GAUGES` sets `L4GAUGE` (so `MakeGaugeRenderer` builds the
|
||||
renderer) and `SVGA16` does NO per-surface D3D (a file-static `DevGaugeComposite()` gate forces the
|
||||
no-surface path in `BuildWindows` + short-circuits `Update`/`Refresh`). ⚠ Reality-check: `FindBestAdapterIndices`
|
||||
hands the gauge renderer **3 non-null indices even on a dev box** (all the primary adapter), so the earlier
|
||||
"passes NULL → 0 surfaces" assumption was wrong — the gate is keyed on `BT_DEV_GAUGES`, not the count.
|
||||
|
||||
Waking the (never-exercised) gauge subsystem exposed a **cascade of 4 dormant-path bugs**, each guarded:
|
||||
1. `SVGA16::Update` (L4VB16.cpp) — indexed empty `mSurfaces[]`; guarded `DevGaugeComposite()||NUM<=0`.
|
||||
2. `SVGA16::Refresh` — same; guarded.
|
||||
3. `LBE4ControlsManager::MakeLinkedLamp` (L4CTRL.cpp:2057) — `GetGaugeRenderer()->GetLampManager()`
|
||||
returns NULL (recon gap in `BTL4GaugeRenderer`); guarded (skip the linked lamp when null).
|
||||
4. `L4GaugeRenderer::NotifyOfNewInterestingEntity` (L4GREND.cpp:440) — `warehousePointer->gaugeImageBin`
|
||||
has a garbage internal chain (recon shadow of the warehouse); guarded (skip the per-mech gauge-IMAGE
|
||||
cache under `BT_DEV_GAUGES`).
|
||||
|
||||
⚠ **KEY FINDING: the gauge subsystem's RECONSTRUCTION IS INCOMPLETE.** It was dormant/never-exercised, so
|
||||
these latent recon bugs (lamp-manager offset, warehouse init) only surfaced on waking it. It now BOOTS
|
||||
stably, but whether the widgets raster MEANINGFUL content into `pixelBuffer` is UNVERIFIED until the
|
||||
composite (Milestone B) draws it. The per-mech gauge IMAGES (armor diagrams via the warehouse) are
|
||||
currently skipped; the radar/secondary map + instruments read the mech directly and *may* render partially.
|
||||
So the beachhead's real question shifted from "composite a working buffer" to "how complete is the gauge
|
||||
recon." FAITHFUL FOLLOW-UPS: fix the `BTL4GaugeRenderer` lamp-manager + warehouse reconstruction (shadowed
|
||||
members) so per-mech gauges + lamps work (needed for real MFD content anyway).
|
||||
|
||||
**Milestone B (NEXT) — the composite pass:** a new `SVGA16` method (has access to `pixelBuffer` + the `sec`
|
||||
port + palette) creates a texture on the MAIN device (`l4_application->GetVideoRenderer()->GetDevice()`),
|
||||
runs the secondary palette-expand (L4VB16.cpp:4124-4147) into it, and the main renderer draws it as an inset
|
||||
quad (state save/restore) after the 3D pass. This is what reveals whether the content is real or garbage.
|
||||
@@ -2054,6 +2054,13 @@ Lamp *
|
||||
L4LampManager
|
||||
*lamp_manager = (L4LampManager *) application->
|
||||
GetGaugeRenderer()->GetLampManager();
|
||||
// DEV-COMPOSITE GUARD: when the gauge renderer is woken (BT_DEV_GAUGES) but its
|
||||
// reconstructed lamp manager isn't wired (GetLampManager returns NULL -- a recon
|
||||
// offset gap in BTL4GaugeRenderer), skip the linked cockpit lamp rather than
|
||||
// constructing an L4Lamp with a NULL manager (crashes in LampManager::AddLamp).
|
||||
// The lamp is a secondary cockpit indicator; inert on the pod (manager valid).
|
||||
if (lamp_manager == NULL)
|
||||
return NULL;
|
||||
Check(lamp_manager);
|
||||
|
||||
Lamp
|
||||
|
||||
@@ -437,9 +437,18 @@ void
|
||||
//----------------------------------------------
|
||||
Check(warehousePointer);
|
||||
|
||||
// DEV-COMPOSITE GUARD: the reconstructed BTL4GaugeRenderer's warehouse / gaugeImageBin
|
||||
// is not fully wired (its internal chain reads garbage -> WarehouseBin::Find AVs).
|
||||
// Skip the per-entity gauge-IMAGE caching when the gauge renderer is woken on a dev
|
||||
// box (BT_DEV_GAUGES); the radar/secondary composite doesn't need per-mech images.
|
||||
// Inert on the pod (the real warehouse is valid). FAITHFUL FOLLOW-UP: fix the
|
||||
// BTL4GaugeRenderer warehouse reconstruction (likely a shadowed warehousePointer).
|
||||
if (getenv("BT_DEV_GAUGES") == NULL)
|
||||
{
|
||||
((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
|
||||
interesting_entity->GetResourceID()
|
||||
);
|
||||
}
|
||||
//----------------------------------------------
|
||||
// Update gaugeRenderer
|
||||
//----------------------------------------------
|
||||
|
||||
@@ -98,6 +98,17 @@
|
||||
// );
|
||||
|
||||
|
||||
// DEV-COMPOSITE (option B): opt-in via BT_DEV_GAUGES. When set, SVGA16 does NO
|
||||
// per-surface D3D output (no per-adapter devices/windows/present) -- it only holds
|
||||
// the CPU pixelBuffer the gauge widgets raster into; the MAIN renderer composites
|
||||
// that buffer as a window inset. The pod multi-surface path is left untouched.
|
||||
static bool DevGaugeComposite()
|
||||
{
|
||||
static int v = -1;
|
||||
if (v < 0) v = (getenv("BT_DEV_GAUGES") != NULL) ? 1 : 0;
|
||||
return v != 0;
|
||||
}
|
||||
|
||||
void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed, int *secondaryIndex, int *aux1Index, int *aux2Index)
|
||||
{
|
||||
NUMGAUGEWINDOWS = 0;
|
||||
@@ -117,8 +128,22 @@ void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed
|
||||
NUMGAUGEWINDOWS++;
|
||||
}
|
||||
|
||||
if (DevGaugeComposite())
|
||||
{
|
||||
// DEV COMPOSITE (option B): force the "no D3D output surfaces" path. Do NOT
|
||||
// create per-surface devices/windows -- FindBestAdapterIndices hands us the
|
||||
// primary adapter (up to 3x) on a dev box, but a 2nd/3rd fullscreen device
|
||||
// can't be created there. The base Video16BitBuffered pixelBuffer still
|
||||
// exists and the widgets raster into it; the main renderer composites it.
|
||||
NUMGAUGEWINDOWS = 0;
|
||||
}
|
||||
|
||||
if (NUMGAUGEWINDOWS == 0)
|
||||
{
|
||||
// (defensive) NULL the per-surface arrays so ~SVGA16 delete[] is safe on the
|
||||
// no-surface path (they are not allocated below).
|
||||
gaugeWindows = NULL; mDevice = NULL; mVertBuffers = NULL;
|
||||
mSurfaces = NULL; mLockFlags = NULL; mPresentParams = NULL; mSurfaceRects = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -202,6 +227,15 @@ void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed
|
||||
{
|
||||
DEBUG_STREAM<<"Couldn't create Direct3D device!"<<std::endl<<std::flush;
|
||||
PostQuitMessage(1);
|
||||
// GUARD (platform profile): mDevice[j] is null/uninitialised on
|
||||
// failure, and the code below (GetDeviceCaps @next line, CreateTexture,
|
||||
// CreateVertexBuffer) derefs it -> segfault. This is hit when the POD
|
||||
// profile runs on a box WITHOUT the pod's extra adapters/monitors (a
|
||||
// second fullscreen gauge device can't be created). PostQuitMessage
|
||||
// already asked to bail; break so we exit the surface loop cleanly
|
||||
// instead of crashing. INERT on real pod hardware (CreateDevice
|
||||
// succeeds there) -- does not change the pod multi-surface behavior.
|
||||
break;
|
||||
}
|
||||
|
||||
D3DCAPS9 caps;
|
||||
@@ -4040,6 +4074,13 @@ Logical SVGA16::Update(Logical forceAll)
|
||||
return False; // Do no more!
|
||||
}
|
||||
|
||||
// DEV-COMPOSITE GUARD (option B): dev-composite SVGA16 does NO per-surface D3D
|
||||
// (BuildWindows forced NUMGAUGEWINDOWS=0 + allocated nothing); the mSurfaces[0]
|
||||
// path below would index an absent array. The main renderer composites the shared
|
||||
// CPU pixelBuffer as a window inset instead. Inert on the pod.
|
||||
if (DevGaugeComposite() || NUMGAUGEWINDOWS <= 0)
|
||||
return False;
|
||||
|
||||
if (++mDisplayToUpdate >= NUMGAUGEWINDOWS)
|
||||
mDisplayToUpdate = 0;
|
||||
|
||||
@@ -4221,6 +4262,12 @@ Logical SVGA16::Update(Logical forceAll)
|
||||
|
||||
void SVGA16::Refresh()
|
||||
{
|
||||
// DEV-COMPOSITE GUARD (option B): dev-composite does NO per-surface D3D; skip the
|
||||
// per-surface clear/present (the main renderer composites the CPU pixelBuffer
|
||||
// inset). Inert on the pod.
|
||||
if (DevGaugeComposite() || NUMGAUGEWINDOWS <= 0)
|
||||
return;
|
||||
|
||||
HRESULT hr;
|
||||
V( mDevice[mDisplayToUpdate]->Clear(0, NULL, D3DCLEAR_TARGET, 0xFF000000, 1.0f, 0) );
|
||||
|
||||
|
||||
+74
-5
@@ -217,18 +217,87 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Environment defaults (dev box: keyboard controls, single window).
|
||||
// ---------------------------------------------------------------------------
|
||||
// PLATFORM PROFILE -- dev (default) vs pod. This is NOT a code fork: the
|
||||
// profile just selects WHICH environment preset the existing video/gauge/input
|
||||
// code reads (the pod multi-surface path already exists -- FindBestAdapterIndices
|
||||
// / SVGA16::BuildWindows / L4GaugeRenderer -- gated on these env vars).
|
||||
// dev = single 800x600 window + keyboard (no L4GAUGE -> MakeGaugeRenderer
|
||||
// returns NULL, btl4app.cpp:353). The working dev build.
|
||||
// pod = multi-surface gauges/MFDs (L4GAUGE set) + RIO cockpit I/O, mirroring
|
||||
// content/SETENV.BAT (the authentic pod preset). The `if getenv==NULL`
|
||||
// guard lets a real pod environment (SETENV.BAT) override every value.
|
||||
// Select with env BT_PLATFORM=pod|dev (robust; primary) or a `-platform pod`
|
||||
// launcher arg. ⚠ A FULL pod run needs the pod's 2 adapters / 7 monitors + RIO
|
||||
// & plasma serial + GAUGE\L4GAUGE.INI (Phase-8, on real hardware). On a dev box
|
||||
// `-platform pod` ATTEMPTS the path (FindBestAdapterIndices degrades to the
|
||||
// primary adapter) -- selectable for bring-up, not fully validated off-pod.
|
||||
// ---------------------------------------------------------------------------
|
||||
int gBTPlatformPod = 0;
|
||||
{
|
||||
const char *pe = getenv("BT_PLATFORM");
|
||||
if (pe && _stricmp(pe, "pod") == 0) gBTPlatformPod = 1;
|
||||
// Convenience: also accept a raw `-platform pod` on the command line,
|
||||
// parsed off lpCmdLine independently of L4Application::ParseCommandLine.
|
||||
const char *pf = lpCmdLine ? strstr(lpCmdLine, "-platform") : 0;
|
||||
if (pf)
|
||||
{
|
||||
char pv[16] = { 0 };
|
||||
if (sscanf(pf + 9, " %15s", pv) == 1 && _stricmp(pv, "pod") == 0)
|
||||
gBTPlatformPod = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (gBTPlatformPod)
|
||||
{
|
||||
// POD profile. RIO cockpit input, which falls back to KEYBOARD when the
|
||||
// serial port isn't present (e.g. a dev box: "RIO initialization failed!
|
||||
// Shutting Down Serial Port!" -> keyboard).
|
||||
if (getenv("L4CONTROLS") == NULL) putenv("L4CONTROLS=RIO,KEYBOARD");
|
||||
|
||||
// The multi-surface gauges/MFDs are a POD-HARDWARE capability: each surface
|
||||
// needs its OWN fullscreen D3D device on one of the pod's 2 video cards
|
||||
// (separate adapters), which can't be created on a typical dev box -- the
|
||||
// L4GaugeRenderer/SVGA16 path bails there. So `-platform pod` does NOT
|
||||
// auto-enable them; it stays bootable everywhere as single-window + the pod
|
||||
// INPUT profile. On the REAL pod the gauges come from content/SETENV.BAT
|
||||
// (which sets L4GAUGE + the gauge/adapter env); the existing, untouched
|
||||
// FindBestAdapterIndices / SVGA16 / L4GaugeRenderer path then runs.
|
||||
// To force-test the surfaces off-pod, set L4GAUGE explicitly (needs >=2 real
|
||||
// display ADAPTERS, not just monitors). FOLLOW-UP for a dev-visible pod HUD:
|
||||
// composite the MFDs/gauges into the single window ("MFD compositing on dev").
|
||||
if (getenv("L4GAUGE") != NULL && getenv("L4PLASMA") == NULL)
|
||||
putenv("L4PLASMA=com2"); // only when the pod env already asked for gauges
|
||||
}
|
||||
else
|
||||
{
|
||||
// DEV profile (default): keyboard controls, single 800x600 window.
|
||||
if (getenv("L4CONTROLS") == NULL) putenv("L4CONTROLS=KEYBOARD");
|
||||
// Renderer config: the real BattleTech DPL environment (paths/fog/lights/
|
||||
// ambient/projection + cached shapes). RP set L4DPLCFG=RPDPL.INI; BT's is
|
||||
// BTDPL.INI (in the pod BT dir). Without this, DPLReadEnvironment falls back
|
||||
// to dpldflt.ini and the cavern world never gets its environment.
|
||||
}
|
||||
|
||||
// Shared renderer/config defaults (both profiles). L4DPLCFG=BTDPL.INI gives
|
||||
// DPLReadEnvironment the real BT environment (paths/fog/lights/ambient) instead
|
||||
// of the dpldflt.ini fallback.
|
||||
if (getenv("L4DPLCFG") == NULL) putenv("L4DPLCFG=BTDPL.INI");
|
||||
if (getenv("DPLARG") == NULL) putenv("DPLARG=1");
|
||||
if (getenv("MULTISAMPLE") == NULL) putenv("MULTISAMPLE=0");
|
||||
if (getenv("TARGETFPS") == NULL) putenv("TARGETFPS=60");
|
||||
if (getenv("MAXPARTICLES")== NULL) putenv("MAXPARTICLES=8192");
|
||||
|
||||
// DEV-COMPOSITE GAUGES (opt-in, default OFF): BT_DEV_GAUGES wakes the (otherwise
|
||||
// dormant) gauge renderer so the pod's gauges/MFDs render + can be composited into
|
||||
// the dev window. Setting L4GAUGE makes MakeGaugeRenderer build the renderer; on a
|
||||
// dev box the pod multi-surface (SVGA16 per-adapter fullscreen devices) stays at 0
|
||||
// surfaces (guarded in SVGA16::Update), and a composite pass in the main renderer
|
||||
// blits the CPU-rastered pixelBuffer as a window inset. See docs/GAUGE_COMPOSITE.md.
|
||||
if (getenv("BT_DEV_GAUGES") != NULL && getenv("L4GAUGE") == NULL)
|
||||
putenv("L4GAUGE=640x480x16");
|
||||
|
||||
std::cout << "[boot] platform profile: "
|
||||
<< (gBTPlatformPod ? "POD (RIO cockpit input; multi-surface gauges/MFDs via pod hardware or explicit L4GAUGE)"
|
||||
: "DEV (single window + keyboard)")
|
||||
<< std::endl << std::flush;
|
||||
|
||||
// DEBUG (bring-up, default OFF): force the player mech to walk full-ahead with
|
||||
// no keypress so locomotion + camera-follow can be verified in a headless run.
|
||||
// BT_FORCE_THROTTLE may carry a VALUE: +1 full run, ~0.3 walk, -1 reverse (default 1.0).
|
||||
|
||||
+25
-2
@@ -2,10 +2,33 @@
|
||||
rem Launch btl4.exe against the bundled content tree.
|
||||
rem The game requires its working directory to be the content dir (BTL4.RES,
|
||||
rem VIDEO\, BTDPL.INI, *.EGG are all resolved relative to cwd).
|
||||
rem Usage: run.cmd [EGG] (default DEV.EGG = grass/day)
|
||||
rem
|
||||
rem Usage: run.cmd [EGG] [pod|dev] (args order-independent)
|
||||
rem EGG a mission descriptor in content\ (default DEV.EGG = grass/day)
|
||||
rem pod | dev platform profile (default dev):
|
||||
rem dev = single 800x600 window + keyboard (the dev test build)
|
||||
rem pod = RIO cockpit input + (on real pod hardware) the
|
||||
rem multi-surface gauges/MFDs. On a dev box pod boots
|
||||
rem single-window (the multi-surface needs the pod's 2
|
||||
rem video cards); it still exercises the pod input/config.
|
||||
rem (Equivalent: set BT_PLATFORM=pod, or pass -platform pod straight to the exe.)
|
||||
setlocal
|
||||
set CFG=Debug
|
||||
set EGG=%1
|
||||
set EGG=
|
||||
set PROF=dev
|
||||
|
||||
:parse
|
||||
if "%~1"=="" goto done
|
||||
if /i "%~1"=="pod" (set PROF=pod& shift& goto parse)
|
||||
if /i "%~1"=="dev" (set PROF=dev& shift& goto parse)
|
||||
set EGG=%~1
|
||||
shift
|
||||
goto parse
|
||||
:done
|
||||
|
||||
if "%EGG%"=="" set EGG=DEV.EGG
|
||||
if /i "%PROF%"=="pod" set BT_PLATFORM=pod
|
||||
|
||||
echo [run] egg=%EGG% profile=%PROF%
|
||||
cd /d "%~dp0..\content"
|
||||
"%~dp0..\build\%CFG%\btl4.exe" -egg %EGG%
|
||||
|
||||
Reference in New Issue
Block a user