HUD aspect correction: square reticle units at any window shape (task #44)

User: "it doesn't scale with our screen resolution?" -- correct. The
HUD draws into the fixed 800x600 backbuffer and the present stretches
it into the client area: on non-4:3 windows everything distorted
(circles -> ellipses, the bottom tape widened vs the ladder, positions
drifted outward).

- BTGetPresentAspect (L4VIDEO, from gWindowAspect; 4:3 fallback).
- dpl2d x-unit = (bbW/2)/presentAspect (== bbH/2 at 4:3 -- unchanged
  there), circles pre-squished in bb space so the stretch restores
  round.
- The reticle<->NDC conversions (BTGetAimRay / BTProjectToReticle /
  BTProjectHotBox / BTTwistToReticleX) use the PRESENT aspect; the
  mouse map (BTClientToReticle) is now pure client-relative.
- Placement itself is authentic and stays put: the binary clusters the
  instruments around the boresight (ladder x=0.35 half-heights, spans
  +-0.25) -- NOT at the display edges.
- Crash en route: the BTGetPresentAspect extern declared INSIDE dpl2d's
  anonymous namespace = a different, unresolved symbol -> /FORCE
  garbage call (landed in MissileLauncher::DefaultData). Moved to
  global scope; gotcha noted in the comment.

Verified 4:3 regression: aimed lock + zone hits + no crash; hud-geom
MapX/MapY unchanged at 4:3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-09 09:14:36 -05:00
co-authored by Claude Fable 5
parent e13e8af44b
commit fa88f74c68
2 changed files with 71 additions and 21 deletions
+23 -13
View File
@@ -139,24 +139,24 @@ void BTSetAimProjection(float p11, float p22, float vpW, float vpH)
}
}
float BTGetPresentAspect(); // defined below with gWindowAspect (task #44)
//
// Client-area mouse position -> reticle coords: undo the present stretch
// (client px -> viewport px), then centre/scale by the dpl2d unit. Clamped
// to the visible frame.
// Client-area mouse position -> reticle coords. The reticle frame is SQUARE
// on the PRESENTED image (task #44): unit = half the CLIENT height on both
// axes -- no backbuffer round-trip. Clamped to the visible frame.
//
void BTClientToReticle(float mx, float my, float cw, float ch,
float *rx, float *ry)
{
if (cw <= 0.0f || ch <= 0.0f || gBTAimVpW <= 0.0f || gBTAimVpH <= 0.0f)
if (cw <= 0.0f || ch <= 0.0f)
{
*rx = 0.0f; *ry = 0.0f;
return;
}
const float vx = mx * (gBTAimVpW / cw);
const float vy = my * (gBTAimVpH / ch);
float x = (vx - gBTAimVpW * 0.5f) / (gBTAimVpH * 0.5f);
float y = (vy - gBTAimVpH * 0.5f) / (gBTAimVpH * 0.5f);
const float xmax = gBTAimVpW / gBTAimVpH;
float x = (mx - cw * 0.5f) / (ch * 0.5f);
float y = (my - ch * 0.5f) / (ch * 0.5f);
const float xmax = cw / ch;
if (x < -xmax) x = -xmax;
if (x > xmax) x = xmax;
if (y < -1.0f) y = -1.0f;
@@ -176,7 +176,7 @@ float BTTwistToReticleX(float twist_rad)
{
if (!gBTAimCamValid || gBTAimP11 <= 0.0f || gBTAimVpH <= 0.0f)
return 0.0f;
return (float)tan((double)twist_rad) * gBTAimP11 * (gBTAimVpW / gBTAimVpH);
return (float)tan((double)twist_rad) * gBTAimP11 * BTGetPresentAspect();
}
//
@@ -190,7 +190,7 @@ int BTGetAimRay(float rx, float ry, float outStart[3], float outDir[3])
if (!gBTAimCamValid || gBTAimP11 <= 0.0f || gBTAimP22 <= 0.0f
|| gBTAimVpW <= 0.0f)
return 0;
const float ndcX = rx * (gBTAimVpH / gBTAimVpW);
const float ndcX = rx / BTGetPresentAspect(); // square reticle frame (task #44)
const float ndcY = -ry;
const float cx = ndcX / gBTAimP11;
const float cy = ndcY / gBTAimP22;
@@ -240,7 +240,7 @@ int BTProjectToReticle(const float world[3], float *rx, float *ry)
}
const float ndcX = (xc * gBTAimP11) / depth;
const float ndcY = (yc * gBTAimP22) / depth;
*rx = ndcX * (gBTAimVpW / gBTAimVpH);
*rx = ndcX * BTGetPresentAspect(); // square reticle frame (task #44)
*ry = -ndcY;
return 1;
}
@@ -273,7 +273,7 @@ int BTProjectHotBox(const float top[3], float *xl, float *xr,
*side = (xc >= 0.0f) ? 1 : -1;
return 0;
}
const float xs = (gBTAimP11 / depth) * (gBTAimVpW / gBTAimVpH);
const float xs = (gBTAimP11 / depth) * BTGetPresentAspect(); // (task #44)
const float ys = gBTAimP22 / depth;
*xl = (xc - 4.0f) * xs;
*xr = (xc + 4.0f) * xs;
@@ -1112,6 +1112,16 @@ void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
// the scene fat/skinny.
float gWindowAspect = 0.0f;
// The PRESENTED aspect (task #45): the fixed 800x600 backbuffer stretches into
// the client area, so anything drawn in backbuffer pixels must pre-compensate
// by the CLIENT aspect to appear square on screen. The HUD + the aim-ray
// reticle<->NDC conversions use this.
float BTGetPresentAspect()
{
extern float gWindowAspect;
return (gWindowAspect > 0.0f) ? gWindowAspect : (800.0f / 600.0f);
}
#define PILL_COUNT 20
#define PILL_SIZE (y_size*0.03125) // 32 @ 1280x1024
#define PILL_SPACING (PILL_SIZE*0.625) // 20 @ 1280x1024