From 0a77d8e54b41c3d27a08dc51df21ca7e12b0daec Mon Sep 17 00:00:00 2001 From: arcattack Date: Sat, 18 Jul 2026 16:32:23 -0500 Subject: [PATCH] Camera-seat ranking window LIVE: the 1995 broadcast overlay reimplemented The stubbed dpl-instance ranking display is reborn as screen-space quads in CameraShipHUDRenderable::Render: - followed-player callsign banner (bottom center; the old direct index was correct -- Execute already converts to the 0-based texture slot) - the RANKING WINDOW: one row per scoring player, [ordinal][callsign] in rank order right of center; visibility = the Director's authentic flash logic (10s on / 15s off, solid final 30s); rows follow LIVE playerRank pointers so they re-sort as scores change - discovery: each 128x32 ordinal bitmap packs TWO ordinals side by side ('1st|2nd', '3rd|4th' -- why 4 bitmaps serve 8 players); draw = texture rank/2 with a u-half selected by rank parity - alpha-blended A4R4G4B4 white-on-transparent textures x green diffuse = the authentic green look BT_SHOT moved AFTER the 2D pass -- it captured the backbuffer pre-HUD, so overlays were on screen but invisible to screenshots (cost one false debugging round). GetOrdinalTexture accessor added. Screenshot-verified: '1st MAVERICK' standings + MAVERICK banner over live auto-directed coverage; 2-node mech smoke PASS (un-regressed; the new render path only executes when a CameraDirector HUD exists). Co-Authored-By: Claude Fable 5 --- content/CAM2.EGG | 120 +++++++++++++++++------------------ context/open-questions.md | 14 +++- engine/MUNGA_L4/L4VIDEO.cpp | 59 +++++++++-------- engine/MUNGA_L4/L4VIDEO.h | 1 + engine/MUNGA_L4/L4VIDRND.cpp | 115 +++++++++++++++++++++++++++++++-- 5 files changed, 214 insertions(+), 95 deletions(-) diff --git a/content/CAM2.EGG b/content/CAM2.EGG index 3a26325..c600b31 100644 --- a/content/CAM2.EGG +++ b/content/CAM2.EGG @@ -162,7 +162,7 @@ pilot=10.99.0.2:1502 hostType=0 advancedDamage=1 loadzones=1 -name=PILOT +name=MAVERICK bitmapindex=1 experience=expert badge=VGL @@ -187,26 +187,9 @@ vehicle=camera vehicleValue=1000 color=White [largebitmap] -bitmap=BitMap::Large::PILOT +bitmap=BitMap::Large::MAVERICK bitmap=BitMap::Large::CAMERA -[BitMap::Large::PILOT] -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 +[BitMap::Large::MAVERICK] bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 @@ -214,6 +197,23 @@ bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 +bitmap=03E0F807C38039FFE3FF8380FC0701E0 +bitmap=03E0F807C1C071FFE3FFC383FF0703C0 +bitmap=03F1F807C1C071FFE3FFE387FF870780 +bitmap=03F1F80EE1C071C00381E38F07C70F00 +bitmap=03B1B80EE0E0E1C00380E38E01871E00 +bitmap=03B1B81EF0E0E1C00380E39E00073C00 +bitmap=03B1B81C70E0E1C00381E39C00077800 +bitmap=03BBB81C7071C1FFC3FFC39C0007FC00 +bitmap=03BBB8383871C1FFC3FF839C0007FC00 +bitmap=039B38383871C1FFC3FE039C0007CE00 +bitmap=039B383FFC3B81C0038F039C00078E00 +bitmap=039B387FFC3B81C00387839E00070700 +bitmap=039F387FFC3B81C00383838E01870780 +bitmap=039F38F01E1F01C00381C38F07C70380 +bitmap=038E38E00E1F01FFE381E387FF8701C0 +bitmap=038E38E00E1F01FFE380E383FF0701E0 +bitmap=038E39C0070E01FFE380F380FC0700E0 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 @@ -232,26 +232,26 @@ bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 -bitmap=00000000000000000000000000000000 +bitmap=003F8007E00FE03F87FFF8FFF800FC00 +bitmap=00FFE007F00FE03F87FFF8FFFC00FE00 +bitmap=01FFF007F00FE03F87FFF8FFFE00FE00 +bitmap=03FFF80FF00FF07F87FFF8FFFF01FE00 +bitmap=07E0F80F780F7077878000F01F01EF00 +bitmap=07807C0F780F7077878000F00F01EF00 +bitmap=0F80301F780F7077878000F00F03EF00 +bitmap=0F00001E3C0F7077878000F01F03C780 +bitmap=0F00001E3C0F38E787FFF0FFFE03C780 +bitmap=0F00003E3C0F38E787FFF0FFFE07C780 +bitmap=0F00003C1E0F38E787FFF0FFFC0783C0 +bitmap=0F00003C1E0F18C787FFF0FFF00783C0 +bitmap=0F00307FFE0F1DC7878000F0F80FFFC0 +bitmap=0F807C7FFF0F1DC7878000F07C0FFFE0 +bitmap=07807C7FFF0F1DC7878000F03C0FFFE0 +bitmap=07E1F8F80F0F0D87878000F03E1F01E0 +bitmap=03FFF8F0078F0F8787FFF8F01F1E00F0 +bitmap=01FFF0F0078F0F8787FFF8F00F1E00F0 +bitmap=00FFE1F0078F070787FFF8F00FBE00F0 +bitmap=003F81E003CF070787FFF8F007FC0078 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 bitmap=00000000000000000000000000000000 @@ -262,21 +262,21 @@ x=128 y=32 width=8 [smallbitmap] -bitmap=BitMap::Small::PILOT +bitmap=BitMap::Small::MAVERICK bitmap=BitMap::Small::CAMERA -[BitMap::Small::PILOT] -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 +[BitMap::Small::MAVERICK] bitmap=0000000000000000 bitmap=0000000000000000 bitmap=0000000000000000 +bitmap=38E3B07FBF31E630 +bitmap=38E398D831B33660 +bitmap=3DE6D8D831B606C0 +bitmap=3DE6D8D831B607C0 +bitmap=3566CD9FBF3607E0 +bitmap=3566CD9833360660 +bitmap=376FED9831B60660 +bitmap=376C671831B33630 +bitmap=326C671FB0F1E630 bitmap=0000000000000000 bitmap=0000000000000000 bitmap=0000000000000000 @@ -288,15 +288,15 @@ width=4 bitmap=0000000000000000 bitmap=0000000000000000 bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 -bitmap=0000000000000000 +bitmap=007871C77EFC3800 +bitmap=00CC71C760C63800 +bitmap=0180D9EF60C66C00 +bitmap=0180D9EF60C66C00 +bitmap=0180D9AB7EFC6C00 +bitmap=0180D9AB60CC6C00 +bitmap=0181FDBB60C6FE00 +bitmap=00CD8DBB60C6C600 +bitmap=00798D937EC3C600 bitmap=0000000000000000 bitmap=0000000000000000 bitmap=0000000000000000 diff --git a/context/open-questions.md b/context/open-questions.md index 0987c3c..094f01b 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -47,9 +47,17 @@ authentic path scoped. — the heat family's "frozen 300" note stands CORRECTED). Verified live: mech+camera 2-seat relay session — replicated player links (`bmp=1+veh`), director locks the goal (30s timer), camera ship tracks the moving mech (`sees=1`) and CUTS between authored cameras - (screenshots). Egg: pilot page `hostType=1` + `vehicle=camera`. Remaining: the - ranking-window overlay draw (L4VIDRND.cpp:3016 stubs), a camera-seat row helper in the - operator app (hostType not in the roster UI), shot-composition polish. Status: core LIVE. + (screenshots). Egg: pilot page `hostType=1` + `vehicle=camera`. **RANKING WINDOW LIVE (same day)**: the stubbed + 1995 overlay reimplemented as screen-space quads in `CameraShipHUDRenderable::Render` — the + followed player's callsign banner (bottom center, the Execute index is ALREADY 0-based) + a + rank-ordered row per scoring player ([ordinal][callsign], right of center), visibility = the + Director's flash logic (10s on/15s off, solid final 30s), live `playerRank` pointers re-sort + rows as scores change. **Each 128×32 ordinal bitmap packs TWO ordinals side by side** + ("1st|2nd", "3rd|4th" — why 4 bitmaps serve 8 players): texture rank/2, u-half by parity. + BT_SHOT now captures AFTER the 2D pass (it used to dump pre-HUD — overlays were on screen + but invisible to screenshots). Screenshot-verified: "1st MAVERICK" + banner over live + coverage. Remaining: operator-app camera-seat row helper (hostType not in the roster UI), + shot-composition polish. Status: broadcast display COMPLETE. ## Pod-hardware readiness gaps (Phase 8 assessment, 2026-07-17) [[pod-hardware]] Everything below EXISTS in the tree but has NEVER run against real pod hardware. Mechanism diff --git a/engine/MUNGA_L4/L4VIDEO.cpp b/engine/MUNGA_L4/L4VIDEO.cpp index af2b08a..5e028e4 100644 --- a/engine/MUNGA_L4/L4VIDEO.cpp +++ b/engine/MUNGA_L4/L4VIDEO.cpp @@ -7431,6 +7431,9 @@ void Check_Pointer(display_rank_window); mCamShipHUD = new CameraShipHUDRenderable(entity, CameraShipHUDRenderable::Dynamic, player_index, display_rank_window); + if (getenv("BT_CAM_LOG")) + DEBUG_STREAM << "[cam] CameraShipHUDRenderable created\n" + << std::flush; } break; } @@ -8518,34 +8521,6 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte BTDrawTranslocationSpheres(mDevice, &viewTransform, (float)dT, mTargetRenderTime); } - // DIAG (off by default): BT_SHOT= dumps this instance's backbuffer (the rendered - // WORLD, before the 2D HUD overlay) to a PNG once ~180 frames in, for non-disruptive - // per-instance frame capture without foregrounding the window. Each instance sets its - // own path. Built for the -net render glitch (task #53) but general. - { - const char *shotPath = getenv("BT_SHOT"); - if (shotPath) - { - static int s_btShotN = 0; - ++s_btShotN; - // dump (overwriting) every 90 frames once past 90, so the file always holds a - // recent frame regardless of load timing / debug fps. - if (s_btShotN >= 90 && (s_btShotN % 90) == 0) - { - IDirect3DSurface9 *bb = 0; - if (SUCCEEDED(mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &bb)) && bb) - { - HRESULT hr = D3DXSaveSurfaceToFileA(shotPath, D3DXIFF_PNG, bb, 0, 0); - bb->Release(); - DEBUG_STREAM << "[btshot] frame " << s_btShotN << " -> " << shotPath - << " hr=0x" << std::hex << (unsigned)hr << std::dec << "\n" << std::flush; - } - else - DEBUG_STREAM << "[btshot] GetBackBuffer FAILED frame " << s_btShotN << "\n" << std::flush; - } - } - } - // The BT targeting reticle / weapon pips (2D screen space, cockpit view // only -- the dpl2d layer; see game/reconstructed/dpl2d.cpp). { @@ -8588,6 +8563,34 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte if (mCamShipHUD) mCamShipHUD->Render(0, &viewTransform); + // DIAG (off by default): BT_SHOT= dumps this instance's backbuffer to a + // PNG every 90 frames -- non-disruptive per-instance frame capture without + // foregrounding the window. Built for the -net render glitch (task #53); + // moved AFTER the 2D pass (2026-07-18, camera-seat bring-up) so captures + // include the HUD overlays (reticle, camera-ship ranking window) the screen + // actually shows. + { + const char *shotPath = getenv("BT_SHOT"); + if (shotPath) + { + static int s_btShotN = 0; + ++s_btShotN; + if (s_btShotN >= 90 && (s_btShotN % 90) == 0) + { + IDirect3DSurface9 *bb = 0; + if (SUCCEEDED(mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &bb)) && bb) + { + HRESULT hr = D3DXSaveSurfaceToFileA(shotPath, D3DXIFF_PNG, bb, 0, 0); + bb->Release(); + DEBUG_STREAM << "[btshot] frame " << s_btShotN << " -> " << shotPath + << " hr=0x" << std::hex << (unsigned)hr << std::dec << "\n" << std::flush; + } + else + DEBUG_STREAM << "[btshot] GetBackBuffer FAILED frame " << s_btShotN << "\n" << std::flush; + } + } + } + // 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); diff --git a/engine/MUNGA_L4/L4VIDEO.h b/engine/MUNGA_L4/L4VIDEO.h index f15c101..5f3cf8b 100644 --- a/engine/MUNGA_L4/L4VIDEO.h +++ b/engine/MUNGA_L4/L4VIDEO.h @@ -379,6 +379,7 @@ public: void SetCurrentFogLimits(float fogNear, float fogFar); LPDIRECT3DTEXTURE9 GetNameTexture(int playerIndex) { return mNameTextures[playerIndex]; } + LPDIRECT3DTEXTURE9 GetOrdinalTexture(int rank) { return mOrdinalTextures[rank]; } inline LPDIRECT3DDEVICE9 GetDevice() { return mDevice; } inline LPD3DXMATRIXSTACK GetMatrixStack() { return m_MatrixStack; } diff --git a/engine/MUNGA_L4/L4VIDRND.cpp b/engine/MUNGA_L4/L4VIDRND.cpp index d8624b6..28b04d6 100644 --- a/engine/MUNGA_L4/L4VIDRND.cpp +++ b/engine/MUNGA_L4/L4VIDRND.cpp @@ -3045,17 +3045,124 @@ void CameraShipHUDRenderable::Execute() VideoRenderable::Execute(); } +// +// BT port (camera-seat bring-up, 2026-07-18): draw one screen-space textured +// quad (u/v 0..1) -- shared by the followed-callsign banner and the ranking +// window rows. The name/ordinal bitmaps load black-on-transparent +// (LoadBitSliceTexture, A4R4G4B4), so alpha blending keys them over the scene. +// +static void CameraHUDDrawQuad( + LPDIRECT3DDEVICE9 device, + LPDIRECT3DTEXTURE9 texture, + float x, float y, float w, float h, + float u0 = 0.0f, float u1 = 1.0f) +{ + if (texture == NULL) + { + return; + } + L4VERTEX_2D_TEX quad[4]; + memset(quad, 0, sizeof(quad)); + const DWORD color = D3DCOLOR_XRGB(0, 128, 0); + for (int i = 0; i < 4; ++i) + { + quad[i].z = 0.0f; + quad[i].rhw = 1.0f; + quad[i].color = color; + } + quad[0].x = x + w; quad[0].y = y; quad[0].u = u1; quad[0].v = 0.0f; + quad[1].x = x; quad[1].y = y; quad[1].u = u0; quad[1].v = 0.0f; + quad[2].x = x + w; quad[2].y = y + h; quad[2].u = u1; quad[2].v = 1.0f; + quad[3].x = x; quad[3].y = y + h; quad[3].u = u0; quad[3].v = 1.0f; + device->SetTexture(0, texture); + device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, quad, + sizeof(L4VERTEX_2D_TEX)); +} + void CameraShipHUDRenderable::Render(int pass, const D3DXMATRIX *viewTransform) { - if (application->GetApplicationState() == Application::RunningMission && oldFollowedPlayerIndex >= 0 && oldFollowedPlayerIndex < MAX_PLAYER_NAMES) + if (application->GetApplicationState() != Application::RunningMission) { - LPDIRECT3DDEVICE9 device = myRenderer->GetDevice(); + return; + } + LPDIRECT3DDEVICE9 device = myRenderer->GetDevice(); + device->SetFVF(L4VERTEX_2D_TEX_FVF); + device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - device->SetFVF(L4VERTEX_2D_TEX_FVF); + // + // The followed player's callsign banner (bottom center). Execute already + // converts the 1-based goalPlayerIndex to the 0-based texture slot. + // + if (oldFollowedPlayerIndex >= 0 && oldFollowedPlayerIndex < MAX_PLAYER_NAMES) + { device->SetStreamSource(0, mVB, 0, sizeof(L4VERTEX_2D_TEX)); - device->SetTexture(0, myRenderer->GetNameTexture(oldFollowedPlayerIndex)); + device->SetTexture(0, + myRenderer->GetNameTexture(oldFollowedPlayerIndex)); device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); } + + // + // THE RANKING WINDOW (the un-stubbed 1995 display): one row per scoring + // player -- [1st/2nd/... ordinal][callsign] -- in RANK order, right of + // center (the old DCS placement: window at x .22 y .16, ordinal left of + // the name). Visibility is the Director's flash logic (10s on / 15s off, + // solid for the final 30s). playerRank[bmp-1] -> &playerRanking (LIVE + // pointers captured at ctor), so the rows re-sort as the score changes. + // + if (getenv("BT_CAM_LOG")) + { + static int s_rlog = 0; + if ((s_rlog++ % 300) == 0) + DEBUG_STREAM << "[cam] HUD render: followed=" + << oldFollowedPlayerIndex + << " rankwin=" << (displayRankingWindow + ? (int)*displayRankingWindow : -1) + << " count=" << playerCount << "\n" << std::flush; + } + if (displayRankingWindow != NULL && *displayRankingWindow + && playerRank != NULL && playerCount > 0) + { + const float width = myRenderer->GetWidth(); + const float height = myRenderer->GetHeight(); + const float nameW = width * 0.20f; + const float ordW = width * 0.10f; + const float rowH = nameW * (32.0f / 128.0f); // bitmap aspect + const float pad = width * 0.01f; + const float x0 = width * 0.55f; + const float y0 = height * 0.16f; + + for (int bmp = 0; bmp < playerCount && bmp < MAX_PLAYER_NAMES; ++bmp) + { + if (playerRank[bmp] == NULL) + { + continue; + } + int rank = *playerRank[bmp]; + if (getenv("BT_CAM_LOG")) + { + static int s_rowlog = 0; + if ((s_rowlog++ % 300) == 0) + DEBUG_STREAM << "[cam] rank row: bmp=" << bmp + << " rank=" << rank << "\n" << std::flush; + } + if (rank < 0 || rank >= MAX_PLAYER_NAMES) + { + continue; // non-scoring (director/camera) + } + const float y = y0 + rank * (rowH * 1.15f); + // each 128x32 ordinal bitmap packs TWO ordinals side by side + // ("1st|2nd", "3rd|4th", ...) -- texture rank/2, u-half by parity + CameraHUDDrawQuad(device, myRenderer->GetOrdinalTexture(rank / 2), + x0, y, ordW, rowH, + (rank & 1) ? 0.5f : 0.0f, (rank & 1) ? 1.0f : 0.5f); + CameraHUDDrawQuad(device, myRenderer->GetNameTexture(bmp), + x0 + ordW + pad, y, nameW, rowH); + } + } + + device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~