Gitea #44: the reticle TARGET NAME PLATE (PNAME1-8) -- the target's callsign under the crosshair

Reconstructed the last deferred piece of the 1995 reticle, decoded from the
Execute disassembly @004cdcf0 [T1]:
 - selection: target_mech+0x190 (owning BTPlayer) -> player+0x1e0
   (playerBitmapIndex), 1-based into the mesh table at this+0x2e8
 - placement: re-placed every frame the aim moves -- scale 0.12 uniform,
   translate (K*retPos.x, K*retPos.y - 0.08, -1.0), K = 1/2.8 (the x87 long
   double @0x4cee64) -- so the label TRACKS THE AIM POINT, not screen centre
 - visibility: the LOCK attribute (this+0x184, cached +0x188) AND an owning
   player; reticle-off / simple-X also hide it
 - art: the plate quad UV-addresses a shared 128x64 bitslice texmap whose
   texels the pod OVERWROTE each mission with the egg's callsign rasters
   (original source commented out at L4VIDEO.cpp:5682-5710) -- the baked
   'PLAYER n' art in BMAP.BSL is only the shipped fallback.  Material colour
   is a neutral (0.5,0.5,0.5): grey-white, not phosphor green.

Port: same geometry in reticle units (0.224 below the aim point, 0.336x0.084)
drawn as the target's egg callsign texture through the reticle's own MapX/MapY
mapping, so it follows the world view in every layout and BT_SHOT captures it.
New: dpl2d_DrawTexturedRect, BTReticleTargetPlate, BTGetPlayerNameTexture.

Also: kRetCaret corrected 0.02f -> 0.025f (_DAT_004cd7f4 read from the binary;
the old value was a T3 guess, 20% small).

KB: gauges-hud + open-questions corrected (the chain was filed as deferred and
mis-attributed to 'the 3D marker'); TWO PNAME consumers now distinguished (this
plate, and CameraShipHUDRenderable's ranking window which shipped 2026-07-18);
new bgf-format section on the plate atlas.

Rig-verified: 2-node relay, BT_GOTO=enemy -> '[hud] name plate: bmp=2 tex=1'
and 'Boreas' rendered under the crosshair on the locked target.  Solo clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166KTsC7ADm7VXEi1HF1jNg
This commit is contained in:
arcattack
2026-07-24 18:36:02 -05:00
co-authored by Claude Opus 5
parent b7107b1020
commit 35c750dc7c
7 changed files with 390 additions and 10 deletions
+105
View File
@@ -572,3 +572,108 @@ void dpl2d_ExecuteList(dpl2d_DISPLAY *list, IDirect3DDevice9 *device)
device->SetRenderState(D3DRS_SRCBLEND, oldSrc);
device->SetRenderState(D3DRS_DESTBLEND, oldDst);
}
//
//#############################################################################
// dpl2d_DrawTexturedRect -- a TEXTURED quad in RETICLE coordinates
//#############################################################################
//
// The reticle's target NAME PLATE (the 1995 PNAMEx.bgf plate) is a UV-mapped
// quad, so it cannot ride the line/polyline display lists above -- but it must
// land in the SAME space, because the binary places it from the same
// `Reticle::reticlePosition` that translates the aim group (reticle Execute
// @004cdede [T1 disasm]). So it reuses this TU's authentic mapping: MapX's
// aspect-corrected x unit + MapY's half-height y unit, in the CURRENT viewport
// (which is the world sub-rect under the cockpit-surround layout, not the whole
// backbuffer -- so the plate follows the view like the rest of the reticle).
//
// cx/cy = quad CENTRE, w/h = full extents, all in reticle units. The texture
// arrives as void* because the caller (game/reconstructed/btl4vid.cpp) carries
// no d3d9 types. State is saved/restored exactly as dpl2d_ExecuteList does
// (leaking a texture stage or the alpha state into the next frame's 3D pass is
// the "floating rocks" class of bug).
//
void dpl2d_DrawTexturedRect(
IDirect3DDevice9 *device,
void *texture,
float cx,
float cy,
float w,
float h,
unsigned long argb)
{
if (device == 0 || texture == 0)
return;
D3DVIEWPORT9 vp;
if (FAILED(device->GetViewport(&vp)))
return;
const float ox = (float)vp.X, oy = (float)vp.Y;
const float vw = (float)vp.Width, vh = (float)vp.Height;
const float xunit = XUnit(vw, vh); // aspect-corrected, as MapX
const float yunit = vh * 0.5f; // as MapY
const float px = MapX(cx, ox, vw, vh);
const float py = MapY(cy, oy, vh);
const float hw = (w * 0.5f) * xunit;
const float hh = (h * 0.5f) * yunit;
struct VertexTex
{
float x, y, z, rhw;
DWORD color;
float u, v;
};
const DWORD kVertexTexFVF =
(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
VertexTex quad[4];
memset(quad, 0, sizeof(quad));
for (int i = 0; i < 4; ++i)
{
quad[i].z = 0.0f;
quad[i].rhw = 1.0f;
quad[i].color = (DWORD)argb; // modulates the plate art (its material colour)
}
// tri-strip order: TR, TL, BR, BL
quad[0].x = px + hw; quad[0].y = py - hh; quad[0].u = 1.0f; quad[0].v = 0.0f;
quad[1].x = px - hw; quad[1].y = py - hh; quad[1].u = 0.0f; quad[1].v = 0.0f;
quad[2].x = px + hw; quad[2].y = py + hh; quad[2].u = 1.0f; quad[2].v = 1.0f;
quad[3].x = px - hw; quad[3].y = py + hh; quad[3].u = 0.0f; quad[3].v = 1.0f;
DWORD oldLighting = 0, oldZ = 0, oldCull = 0;
DWORD oldAlpha = 0, oldSrc = 0, oldDst = 0, oldFog = 0;
device->GetRenderState(D3DRS_LIGHTING, &oldLighting);
device->GetRenderState(D3DRS_ZENABLE, &oldZ);
device->GetRenderState(D3DRS_CULLMODE, &oldCull);
device->GetRenderState(D3DRS_ALPHABLENDENABLE, &oldAlpha);
device->GetRenderState(D3DRS_SRCBLEND, &oldSrc);
device->GetRenderState(D3DRS_DESTBLEND, &oldDst);
device->GetRenderState(D3DRS_FOGENABLE, &oldFog);
IDirect3DBaseTexture9 *oldTex = 0;
device->GetTexture(0, &oldTex);
device->SetRenderState(D3DRS_LIGHTING, FALSE);
device->SetRenderState(D3DRS_ZENABLE, FALSE);
device->SetRenderState(D3DRS_FOGENABLE, FALSE);
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
device->SetFVF(kVertexTexFVF);
device->SetTexture(0, (IDirect3DTexture9 *)texture);
device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, quad, sizeof(VertexTex));
device->SetTexture(0, oldTex);
if (oldTex) oldTex->Release();
device->SetFVF(kVertex2DFVF);
device->SetRenderState(D3DRS_LIGHTING, oldLighting);
device->SetRenderState(D3DRS_ZENABLE, oldZ);
device->SetRenderState(D3DRS_FOGENABLE, oldFog);
device->SetRenderState(D3DRS_CULLMODE, oldCull);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, oldAlpha);
device->SetRenderState(D3DRS_SRCBLEND, oldSrc);
device->SetRenderState(D3DRS_DESTBLEND, oldDst);
}