Boresight parallax FIXED + the #4/#5 verdict instrumentation (Gitea #16, #4, #5)

PARALLAX (#16): the pick/fire ray was anchored at mech.y+5.0 (a port
improvisation) while the sight line ran from the eyepoint (y=6.23) --
two parallel rays whose constant offset grew into the reported low-miss
as range closed (measured ry +0.072 @50u -> +1.54 @2.7u).  The decomp's
sight and pick share the eye origin (HudSimulation @4b7830 chain).  Fix:
the viewpoint mech's cockpit eye owns the aim-camera publish in BOTH
views, origin = its own eye translation; leveling + deliberate elevation
untouched; chase view now converges to cockpit ballistics (V cannot
change where shots land).  After: pick pinned to the crosshair (ry <=
5e-6) from 50u to point-blank; 26 laser + 8 missile center-mass hits at
3/4-screen.  Awaiting the reporters' approach-test.

VERDICT INSTRUMENTATION (#4 closed authentic, #5 verdict posted):
BT_RANGE_LOG per-frame pick tracing + BTGroundRayHitExact analytic
cross-check (0/8000 arena fall-throughs; cavern 6/8400 single-frame
grazes -- the 'crazy sliding' is the authentic world-pick + 500 m/s
slide over depth discontinuities); BT_AUD_TAIL StopNote/fade timing +
BT_FIRE_PULSE single-shot driver (the energy 'buzz' is the AUTHORED
charging loop: crescendo through recharge, 1.309s authored release,
measured within one frame).  CAVERN.EGG: solo cavern test egg.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-20 14:51:54 -05:00
co-authored by Claude Fable 5
parent d7158f1f82
commit 70eea6c1a4
13 changed files with 744 additions and 18 deletions
+87 -12
View File
@@ -16,6 +16,12 @@ float gBTEyeTwist = 0.0f;
// pitch from the pick ray -- this is the pilot's DELIBERATE pitch, re-applied
// after the leveling so aimed shots follow the R/F (stick-Y) elevation.
float gBTEyeElev = 0.0f;
// issue #16 (boresight parallax): non-zero while a BORESIGHT eye (the
// viewpoint mech's cockpit siteeyepoint, marked by SetBoresightEye) is alive
// and publishing the aim camera. While set, the active-camera fallback
// publish is suppressed -- the cockpit eyepoint owns the pick/sight ray in
// BOTH views, so toggling V cannot change ballistics.
int gBTBoresightEyeLive = 0;
#pragma hdrstop
#include "l4vidrnd.h"
@@ -5492,6 +5498,7 @@ DPLEyeRenderable::DPLEyeRenderable(
myEntity = This_Entity;
// myParentDCS = Parent_DCS;
myOrientationMatrix = Offset_Matrix;
mIsBoresightEye = false; // issue #16: set via SetBoresightEye
myEyepointRotation = eyepoint_rotation;
if(myEyepointRotation)
{
@@ -5610,6 +5617,30 @@ DPLEyeRenderable::~DPLEyeRenderable()
//// but Phil is making a patch to allow us to check that.
//// dpl_RemoveDCSFromDCS(myParentDCS, myDCS);
// dpl_DeleteDCS(myDCS);
// issue #16: a deleted boresight eye releases the aim-camera publish so
// the active camera's fallback path takes over (spectator / teardown)
// instead of the aim ray going permanently stale.
if (mIsBoresightEye)
{
extern int gBTBoresightEyeLive;
gBTBoresightEyeLive = 0;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SetBoresightEye (issue #16 boresight parallax): mark this eye as the
// VIEWPOINT mech's cockpit eyepoint -- the owner of the aim-camera (pick/
// sight ray) publish in BOTH views. Tracks the process-wide latch that
// gates the active-camera fallback publish (a viewpoint without a cockpit
// eyepoint, e.g. a spectator camera).
//
void
DPLEyeRenderable::SetBoresightEye(bool is_boresight)
{
extern int gBTBoresightEyeLive;
mIsBoresightEye = is_boresight;
if (is_boresight)
gBTBoresightEyeLive = 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TestInstance for the DPLEyeRenderable
@@ -5805,13 +5836,42 @@ void
{
myDevice->SetTransform(D3DTS_VIEW, &view);
// issue #16 diag: publish the RENDER camera pose (the frame the
// crosshair is drawn in) for BT_PARALLAX_LOG's pick-point
// back-projection (BTProjectToRenderView). eyeW rows are
// right/up/BACK + translation (view = inverse(eyeW)).
{
extern void BTSetRenderCamera(const float pos[3], const float xax[3],
const float yax[3], const float zax[3]);
const float rp[3] = { eyeW._41, eyeW._42, eyeW._43 };
const float rxx[3] = { eyeW._11, eyeW._12, eyeW._13 };
const float ryy[3] = { eyeW._21, eyeW._22, eyeW._23 };
const float rzz[3] = { eyeW._31, eyeW._32, eyeW._33 };
BTSetRenderCamera(rp, rxx, ryy, rzz);
}
if (dbgEyeExec < 8)
DEBUG_STREAM << "[EYE] ACTIVE eye " << (void *)this
<< " (mCamera match) pos.y=" << pos.y << "\n" << std::flush;
}
// aim-ray feed (task #36): publish this eye's world pose --
// the LookAtRH basis (zaxis = back, view direction = -zaxis)
// -- for the reticle pick ray / designator projection.
// aim-ray feed (task #36): publish this eye's world pose --
// the LookAtRH basis (zaxis = back, view direction = -zaxis)
// -- for the reticle pick ray / designator projection.
//
// OWNERSHIP (issue #16, boresight parallax): the publish is owned by
// the BORESIGHT eye -- the viewpoint mech's cockpit siteeyepoint --
// in BOTH views (it executes in the mech's draw traversal every
// frame, active camera or not), so the chase view's ballistics are
// IDENTICAL to the cockpit's and the V toggle cannot change where a
// shot lands. Only when no boresight eye is alive (spectator /
// teardown) does the ACTIVE camera publish, with the legacy entity
// anchor as the origin.
{
extern int gBTBoresightEyeLive;
const bool aimOwner = mIsBoresightEye
|| (!gBTBoresightEyeLive && myRenderer->mCamera == this);
if (aimOwner)
{
extern void BTSetAimCamera(const float pos[3], const float xax[3],
const float yax[3], const float zax[3]);
@@ -5865,16 +5925,31 @@ void
D3DXVec3Normalize(&xax, &xax);
D3DXVec3Cross(&yax, &zax, &xax);
}
// ORIGIN: the guns fire from the MECH's torso, not the render
// eye. In CHASE view the eye sits behind+above the mech, so a
// ray from `pos` would start over the shoulder (and, being level,
// fly above a ground target). Anchor the boresight at the mech's
// XZ + a torso/gun height so the pick works in BOTH views (chase
// is the default; cockpit toggles with V). In cockpit view this
// is ~the eyepoint anyway.
float p[3] = { pos.x, pos.y, pos.z };
if (myEntity != 0)
// ORIGIN (issue #16, boresight parallax): the pick/sight ray
// starts at the TRUE EYEPOINT -- this eye's own world
// translation (eyeW row 4, the same origin the crosshair's
// view projects from). The binary's sight math runs in the
// eyepoint-segment frame: HudSimulation computes the
// designator HotBoxVector by transforming the target position
// with inverse(eyeSegToWorld @00424da8) o inverse(
// R(EyepointRotation)) (@004b7830, part_013.c:5688-5702), the
// VIEW is inverse(the live jointtorso->jointeye->siteeyepoint
// chain) (FUN_004c22c4 @part_013.c:11742), and the fired beam
// CONVERGES from the muzzle segment to the picked point
// (@004bace8:7742-7752) -- so pick ray and sight line share
// the eye origin. The old `mech.y + 5.0` anchor (a port
// improvisation) sat BELOW the sight line: a constant
// vertical offset whose angular error grew as range closed --
// shots landing LOW under the target up close (Gitea #16).
// Measured pre-fix: the pick point slid down the reticle
// ry=+0.07 @ 50u -> +0.32 @ 10u -> +1.5 @ 2.7u.
float p[3] = { eyeW._41, eyeW._42, eyeW._43 };
if (!mIsBoresightEye && myEntity != 0)
{
// no cockpit eyepoint alive (spectator/camera director):
// legacy entity anchor -- this camera sits behind/above,
// so a ray from its own origin would start over the
// shoulder.
p[0] = myEntity->localToWorld(3,0);
p[1] = myEntity->localToWorld(3,1) + 5.0f; // feet -> torso/gun height
p[2] = myEntity->localToWorld(3,2);