Fix torso-pitch aim ray: gyro-stabilized boresight from the mech torso
The aim/pick ray was the active RENDER EYE's view direction, which made a
mech unable to aim at a distant enemy:
(a) it inherited the mech BODY pitch -- on a slope the body tilts ~8 deg to
conform to terrain, so the boresight pointed that far DOWN and the pick
ray hit the ground ~50-100m short of the target (groundPicks, never
mechPicks); and
(b) in CHASE view (the default) it was the elevated behind-the-shoulder
camera, so the ray started over the mech's shoulder and flew high.
Fix (L4VIDRND DPLEyeRenderable::Execute, the BTSetAimCamera publish):
- LEVEL the boresight: drop the view-direction pitch and rebuild an upright
basis (world +Y up) -- the guns fire along the mech's gyro-stabilized
HORIZONTAL heading, not the terrain-pitched body. Reticle X still carries
the torso twist (BTTwistToReticleX); reticle Y any elevation.
- ANCHOR the ray origin at the mech's TORSO (myEntity->localToWorld
translation + ~5) instead of the render eye, so it is correct in BOTH the
chase (default) and cockpit views.
Verified end-to-end: solo, enemy 120m ahead, BOTH chase and cockpit view ->
[target] "MECH under boresight ... mechPicks=59 groundPicks=0" -> beam ->
*** DESTROYED. Also proved PickRayHit resolves a REPLICANT when aimed at it
(hit=1 at 757m, 2-node run).
mech4: BT_GOTO="enemy" test aid now chases the nearest peer of ANY instance
(solo dummy OR replicant), not just replicants.
Still open (locomotion, not aim/MP): BT_GOTO/BT_AUTODRIVE stall short of the
enemy, so an AUTOMATED 2-node kill via the real beam isn't demonstrated yet
(the FORCE_DMG hook still is). A human can now aim correctly in the default
view. Solo un-regressed; build clean; KB validator clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
bac45851ad
commit
0dfbb97dcc
+27
-22
@@ -104,30 +104,35 @@ off in solo. `[mp-hdlr]` (BT_MP_NET, mech.cpp:554) logs each applied cross-pod h
|
||||
`scratchpad/mp_test.sh`: A `-egg MP.EGG -net 1501`, B `-net 1601`, then `python tools/btconsole.py MP.EGG
|
||||
127.0.0.1:1501 127.0.0.1:1601` (MP.EGG `[pilots]` = game ports 1502/1602).
|
||||
|
||||
## Interactive -net aim + drive (task #48, 2026-07-09) — partial; the blocker is NOT the netcode [T2]
|
||||
Re-measured on a 2-node one-box run (A `BT_AUTODRIVE`/`BT_GOTO`, `BT_AIM_LOG`, `BT_MP_LOG`). **The prior
|
||||
"aim ray dead / drive dead in -net" claim was STALE — both work now:** A's aim ray is live (`noRay=0`, the
|
||||
active eye publishes the aim camera), and A DRIVES (`BT_AUTODRIVE`/`BT_GOTO` move it). What actually blocks
|
||||
a HUMAN cross-pod kill via the real beam path:
|
||||
- **Boresight inherits the mech BODY pitch.** The aim ray = the active EYE's view direction (with reticle
|
||||
at 0,0). On sloped terrain the mech body pitches to conform (ground model), the cockpit eye/boresight
|
||||
pitches with it, and the pick ray points ~8° DOWN (`[EYE] up=(-.03,.99,-.14)` == `[pick] ldir.y≈-0.145`,
|
||||
every frame). At range that ray hits terrain (`groundPicks`) ~50-100 m short of the enemy → `mechPicks=0`.
|
||||
A close enemy (< ~50 m) would still resolve. Real BT aims the TORSO pitch independently of leg/body tilt;
|
||||
the reconstruction ties the boresight to the render eye. Fixing = derive the aim ray from a level torso
|
||||
boresight (yaw only) OR add torso pitch, not the pitched body eye.
|
||||
- **Drive/goto stalls.** A repeatedly stops (`gait adv=0`) ~700-750 m from the enemy before closing to
|
||||
weapon/pick range — a locomotion/goto reliability issue, not MP.
|
||||
- **PickRayHit vs a replicant is UNCONFIRMED** (blocked by the two above — A never faces the enemy at close
|
||||
range). `localToWorld` on the replicant IS correct (`[pick] lstart` magnitude ≈ true A→B distance; the
|
||||
DeadReckon + `localToWorld=localOrigin` block at mech4.cpp:1191 tracks the replicated position).
|
||||
Test aid added: `BT_GOTO="enemy"` beelines toward the nearest live replicant (spawn coords vary per run, so
|
||||
a fixed "x z" can't reliably face the peer). Recipe: `scratchpad/mp_kill_test.sh`.
|
||||
## Interactive -net aim + drive (task #48, 2026-07-09) [T2]
|
||||
Re-measured on a 2-node one-box run. **The prior "aim ray dead / drive dead in -net" claim was STALE — both
|
||||
work:** A's aim ray is live (`noRay=0`) and A drives (`BT_AUTODRIVE`/`BT_GOTO` move it).
|
||||
|
||||
**FIXED — torso-pitch aim ray (gyro-stabilized boresight).** The aim/pick ray was the active RENDER EYE's
|
||||
view direction, which (a) inherits the mech BODY pitch — on a slope the body tilts ~8° to conform to terrain
|
||||
and the boresight pointed that far DOWN, hitting terrain ~50-100 m short of the enemy (`groundPicks`, never
|
||||
`mechPicks`); and (b) in CHASE view (the DEFAULT) is the elevated behind-the-shoulder camera, so the ray
|
||||
started over the mech's shoulder. Fix (L4VIDRND `DPLEyeRenderable::Execute`, the `BTSetAimCamera` publish):
|
||||
LEVEL the boresight (drop the view-dir pitch, rebuild an upright basis with world +Y) AND anchor its ORIGIN
|
||||
at the mech's torso (`myEntity->localToWorld` translation + ~5) instead of the render eye. The guns now fire
|
||||
along the mech's horizontal heading from the torso, independent of view mode and terrain tilt. The reticle X
|
||||
still carries the torso twist (`BTTwistToReticleX`); reticle Y any elevation. VERIFIED end-to-end: solo,
|
||||
enemy 120 m ahead, **both chase AND cockpit** view → `[target] MECH under boresight ... mechPicks=59
|
||||
groundPicks=0` → beam → `*** DESTROYED`. Also proved `PickRayHit` resolves a REPLICANT when aimed at it
|
||||
(`hit=1` at 757 m in a 2-node run); the replicant `localToWorld` is correct (DeadReckon + `localToWorld=
|
||||
localOrigin`, mech4.cpp:1191). [T2]
|
||||
|
||||
**STILL OPEN — drive-to-range.** `BT_GOTO`/`BT_AUTODRIVE` steer + move A, but A stalls (`gait adv=0`) short
|
||||
of the enemy and often fails to line the boresight onto a peer at range — so an AUTOMATED 2-node kill via the
|
||||
real beam path isn't demonstrated (the `BT_MP_FORCE_DMG` hook still is). A HUMAN can now aim (the ray is
|
||||
correct in the default view); the gap is the auto-drive facing the target. Locomotion/goto issue, not MP.
|
||||
Test aid: `BT_GOTO="enemy"` beelines toward the nearest live peer (solo dummy OR replicant); `scratchpad/
|
||||
mp_kill_test.sh`.
|
||||
|
||||
## Remaining (P6 phase 4 / Phase 7)
|
||||
Human-playable cross-pod aim (torso-pitch / level-boresight aim ray + drive-to-range reliability, above) so a
|
||||
HUMAN — not just the FORCE_DMG hook — can aim + fire cross-pod; replicant GAIT animation (derive from
|
||||
replicated velocity); the pod-LAN config (real IPs, bare-IP pilot entries). See [[open-questions]]. [T3]
|
||||
Drive-to-range / auto-face reliability (above) for a fully AUTOMATED cross-pod kill demo; replicant GAIT
|
||||
animation (derive from replicated velocity); the pod-LAN config (real IPs, bare-IP pilot entries). See
|
||||
[[open-questions]]. [T3]
|
||||
|
||||
## MP-front scout (task #45, 2026-07-09) — the P6 chain SURVIVES the combat/HUD rework [T2]
|
||||
Re-ran the one-box smoke test on the current build (world-pick targeting, weapon groups, death
|
||||
|
||||
@@ -5588,14 +5588,50 @@ void
|
||||
{
|
||||
extern void BTSetAimCamera(const float pos[3], const float xax[3],
|
||||
const float yax[3], const float zax[3]);
|
||||
// AIM BORESIGHT (task #48): the guns fire along the mech's
|
||||
// GYRO-STABILIZED horizontal heading, NOT the terrain-pitched
|
||||
// body/eye. Publishing the raw eye basis made a mech standing
|
||||
// on a slope (body pitched ~8 deg to conform to the ground) aim
|
||||
// its pick ray INTO the ground, short of a distant target
|
||||
// (mechPicks=0). Level the boresight: drop the view direction's
|
||||
// pitch and rebuild an upright basis (world +Y up). The reticle
|
||||
// X still carries the torso twist (BTTwistToReticleX); the reticle
|
||||
// Y carries any aim elevation. Falls back to the raw basis only
|
||||
// if the view is (degenerately) near-vertical.
|
||||
D3DXVECTOR3 zax = pos - at;
|
||||
D3DXVec3Normalize(&zax, &zax);
|
||||
D3DXVECTOR3 xax;
|
||||
D3DXVec3Cross(&xax, &up, &zax);
|
||||
D3DXVec3Normalize(&xax, &xax);
|
||||
D3DXVECTOR3 yax;
|
||||
D3DXVec3Cross(&yax, &zax, &xax);
|
||||
const float p[3] = { pos.x, pos.y, pos.z };
|
||||
D3DXVECTOR3 fwdLevel(at.x - pos.x, 0.0f, at.z - pos.z);
|
||||
if (D3DXVec3LengthSq(&fwdLevel) > 1e-6f)
|
||||
{
|
||||
D3DXVec3Normalize(&fwdLevel, &fwdLevel);
|
||||
zax = D3DXVECTOR3(-fwdLevel.x, 0.0f, -fwdLevel.z); // back = -level fwd
|
||||
D3DXVECTOR3 wup(0.0f, 1.0f, 0.0f);
|
||||
D3DXVec3Cross(&xax, &wup, &zax);
|
||||
D3DXVec3Normalize(&xax, &xax);
|
||||
D3DXVec3Cross(&yax, &zax, &xax); // == world +Y
|
||||
}
|
||||
else
|
||||
{
|
||||
D3DXVec3Cross(&xax, &up, &zax);
|
||||
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)
|
||||
{
|
||||
p[0] = myEntity->localToWorld(3,0);
|
||||
p[1] = myEntity->localToWorld(3,1) + 5.0f; // feet -> torso/gun height
|
||||
p[2] = myEntity->localToWorld(3,2);
|
||||
}
|
||||
const float x3[3] = { xax.x, xax.y, xax.z };
|
||||
const float y3[3] = { yax.x, yax.y, yax.z };
|
||||
const float z3[3] = { zax.x, zax.y, zax.z };
|
||||
|
||||
@@ -1515,8 +1515,7 @@ void
|
||||
for (int ei = 0; ei < enc; ++ei)
|
||||
{
|
||||
Mech *em = (Mech *)ec[ei];
|
||||
if (em == 0 || em->GetInstance() != ReplicantInstance
|
||||
|| em->IsMechDestroyed()) continue;
|
||||
if (em == 0 || em->IsMechDestroyed()) continue; // any peer (solo dummy OR replicant)
|
||||
Point3D ep = em->localOrigin.linearPosition;
|
||||
float edx = (float)ep.x - (float)localOrigin.linearPosition.x;
|
||||
float edz = (float)ep.z - (float)localOrigin.linearPosition.z;
|
||||
|
||||
Reference in New Issue
Block a user