BT410 Phase 5.3.3: torso weapon-elevation aim -- completes the control surface

Reconstructs Torso::TorsoSimulation (installed as the Torso's per-frame
Performance) and wires the torso aim into MechControlsMapper::InterpretControls,
so the control surface now covers aiming as well as locomotion.

- TORSO: analogElevationAxis/analogTwistAxis + SetAnalog* setters,
  CurrentElevation()/GetHorizontalEnabled() accessors. TorsoSimulation slews
  currentElevation += analogElevationAxis*baseElevationRate*dt clamped to the
  vertical limits, and (if horizontalEnabled) currentTwist by the twist axis
  clamped to the horizontal limits (authentic torso.cpp core; the skeleton-joint
  application is deferred with the render wave).
- InterpretControls: routes stick pitch (stick_y, squared) into the torso
  elevation every mode; Std/Vet route stick yaw into the twist. BT_FORCE_ELEV
  dev hook.

Verified: BT_FORCE_ELEV=0.8 -> the elevation slews up and clamps at the authentic
verticalLimitTop = 0.349 rad = 20deg; neutral -> 0; zero Fail.

Deferred: skeleton-joint application (render wave), HUD free-aim slew,
look/eyepoint commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-21 16:02:54 -05:00
co-authored by Claude Fable 5
parent 84ea322a18
commit 3a0920acc0
5 changed files with 157 additions and 13 deletions
+35
View File
@@ -19,6 +19,10 @@
# include <mech.hpp>
#endif
#if !defined(TORSO_HPP)
# include <torso.hpp>
#endif
Derivation
MechControlsMapper::ClassDerivations(
Subsystem::ClassDerivations,
@@ -184,8 +188,15 @@ void
{
stickPosition.x = (Scalar)atof(force_turn);
}
const char *force_elev = getenv("BT_FORCE_ELEV");
if (force_elev != NULL)
{
stickPosition.y = (Scalar)atof(force_elev);
}
}
Torso *torso = (Torso *)mech->GetTorsoSubsystem();
Scalar topSpeed = mech->GetReverseStrideLength();
Scalar walkSpeed = mech->GetWalkStrideLength();
@@ -209,17 +220,39 @@ void
{
stick_x = -stick_x;
}
Scalar stick_y = stickPosition.y * stickPosition.y;
if (stickPosition.y < 0.0f)
{
stick_y = -stick_y;
}
Scalar pedal_3 = pedalsPosition * pedalsPosition * pedalsPosition;
turnDemand = 0.0f;
//
// Torso aim. Every control mode routes the stick pitch (stick_y) into the
// torso weapon-elevation axis. In Basic the stick yaw is the turn (legs);
// in Standard/Veteran the stick yaw is the torso twist (free aim) and the
// pedals steer. (The HUD free-aim slew + look/eyepoint commit are the
// aiming/camera wave, deferred.)
//
if (controlMode == BasicMode)
{
turnDemand = stick_x;
if (torso != NULL)
{
torso->SetAnalogElevationAxis(stick_y);
torso->SetAnalogTwistAxis(0.0f);
}
}
else
{
turnDemand = pedal_3;
if (torso != NULL)
{
torso->SetAnalogElevationAxis(stick_y);
torso->SetAnalogTwistAxis(stick_x);
}
}
//
@@ -248,9 +281,11 @@ void
DEBUG_STREAM << "[mppr] thr=" << throttlePosition
<< " rev=" << reverseThrust
<< " stickX=" << stickPosition.x
<< " stickY=" << stickPosition.y
<< " mode=" << controlMode
<< " -> speedDemand=" << speedDemand
<< " turnDemand=" << turnDemand
<< " torsoElev=" << (torso ? torso->CurrentElevation() : 0.0f)
<< endl << flush;
}
}
+16 -7
View File
@@ -35,11 +35,20 @@ drive are exercisable without hardware. `BT_MECH_LOG` prints a 1 Hz `[mppr]`
demand trace. Verified: `throttle=1.0` → speedDemand=30; `throttle=0.3,turn=1.0`
→ the mech walks a circle; neutral → 0. See MECH.NOTES.md (Phase 5.3 inc 2).
## Deferred (the aiming wave)
## Torso aim wired (2026-07-21)
The torso/free-look half of the authentic InterpretControls is not yet
reconstructed: the torso analog axes (`Torso::SetAnalogElevationAxis/
SetAnalogTwistAxis`), HUD free-aim slew (`HUD::SetFreeAimSlew`), the recenter
command, and the look/eyepoint commit (`BTCommitLookState`). These are the
aiming/camera surface, separate from locomotion; they come with the
Torso/HUD/eyepoint wave.
InterpretControls now routes the stick PITCH (`stick_y`, squared soft response)
into the torso weapon-elevation axis every control mode
(`Torso::SetAnalogElevationAxis`), and in Standard/Veteran the stick YAW into the
torso twist (`SetAnalogTwistAxis`); Basic keeps the stick yaw as the leg turn.
`Torso::TorsoSimulation` slews + clamps the elevation/twist (see TORSO.NOTES.md).
Verified: `BT_FORCE_ELEV=0.8` → torsoElev clamps at the authentic 20° limit.
## Deferred (the free-aim / camera wave)
Still not reconstructed: the HUD free-aim slew (`HUD::SetFreeAimSlew`, the
Standard/Veteran torso-disabled path), the torso recenter command, and the
look/eyepoint commit (`BTCommitLookState` — re-aims the eyepoint + re-arms the
per-view weapon fire enables). These are the free-aim/camera surface; they come
with the HUD/eyepoint wave. The skeleton-joint application of the torso angles
is deferred with the render wave (TORSO.NOTES.md).
+54 -4
View File
@@ -75,11 +75,22 @@ Torso::Torso(
currentTwist = 0.0f;
currentElevation = 0.0f;
for (int i = 0; i < 24; ++i)
analogElevationAxis = 0.0f;
analogTwistAxis = 0.0f;
for (int i = 0; i < 22; ++i)
{
dynamicsState[i] = 0.0f;
}
//
// Install the per-frame twist/elevation Performance (the replicant copy is
// driven by console updates via TorsoCopySimulation instead, still staged).
//
if (owner->GetInstance() != Entity::ReplicantInstance)
{
SetPerformance(&Torso::TorsoSimulation);
}
Check_Fpu();
}
@@ -100,12 +111,51 @@ Logical
}
//
// Per-frame torso twist / elevation integration. Not yet reconstructed.
//#############################################################################
// TorsoSimulation -- per-frame torso twist / weapon-elevation integration.
//
// Slews the current elevation/twist toward the analog aim axes (written by
// MechControlsMapper::InterpretControls) at the resource base rates, clamped to
// the resource limits. Authentic core (torso.cpp @004b6xxx): current += axis *
// baseRate * dt. APPLYING the resolved angles onto the skeleton torso/gun
// joints (horizontalJointNode / the elevation joint) is deferred with the
// skeleton-link + render wave; here we advance the scalar aim state (read by the
// HUD reticle / weapon aim and, once wired, the joints).
//#############################################################################
//
void
Torso::TorsoSimulation(Scalar)
Torso::TorsoSimulation(Scalar time_slice)
{
Fail("Torso::TorsoSimulation -- torso.cpp not yet reconstructed");
Check(this);
//
// Elevation (weapon pitch): slew toward the analog axis, clamp to limits.
//
currentElevation += analogElevationAxis * baseElevationRate * time_slice;
{
Scalar lo = (verticalLimitBottom < verticalLimitTop)
? verticalLimitBottom : verticalLimitTop;
Scalar hi = (verticalLimitBottom < verticalLimitTop)
? verticalLimitTop : verticalLimitBottom;
if (currentElevation < lo) currentElevation = lo;
if (currentElevation > hi) currentElevation = hi;
}
//
// Twist (horizontal torso rotation): only mechs with an enabled torso joint.
//
if (horizontalEnabled)
{
currentTwist += analogTwistAxis * baseTwistRate * time_slice;
Scalar lo = (horizontalLimitLeft < horizontalLimitRight)
? horizontalLimitLeft : horizontalLimitRight;
Scalar hi = (horizontalLimitLeft < horizontalLimitRight)
? horizontalLimitRight : horizontalLimitLeft;
if (currentTwist < lo) currentTwist = lo;
if (currentTwist > hi) currentTwist = hi;
}
Check_Fpu();
}
void
+18 -2
View File
@@ -68,7 +68,21 @@
Logical
TestInstance() const;
Scalar
CurrentTwist() const { return currentTwist; }
CurrentTwist() const { Check(this); return currentTwist; }
Scalar
CurrentElevation() const { Check(this); return currentElevation; }
Logical
GetHorizontalEnabled() const{ Check(this); return horizontalEnabled; }
//
// Analog aim inputs (written by MechControlsMapper::InterpretControls;
// consumed by TorsoSimulation to slew the twist/elevation each frame).
//
void
SetAnalogElevationAxis(Scalar axis) { Check(this); analogElevationAxis = axis; }
void
SetAnalogTwistAxis(Scalar axis) { Check(this); analogTwistAxis = axis; }
void
TorsoSimulation(Scalar time_slice);
void
@@ -107,11 +121,13 @@
void *horizontalShadowJointNode;
Scalar currentTwist;
Scalar currentElevation;
Scalar analogElevationAxis;
Scalar analogTwistAxis;
//
// Twist/elevation dynamics accumulators advanced by TorsoSimulation.
// Reserved until the per-frame sim is reconstructed (phase 5).
//
Scalar dynamicsState[24];
Scalar dynamicsState[22];
};
#endif
+34
View File
@@ -0,0 +1,34 @@
# TORSO.CPP / .HPP — reconstruction notes
`Torso` (: PowerWatcher) drives torso twist (horizontal) and weapon elevation
(vertical). Tuning rates/limits stream from the resource
(`horizontal/verticalRotationPerSecond`, `horizontal/verticalLimit*`,
`torsoHorizontalEnabled`).
## Reconstructed
- Ctor: streams the base rates (deg→rad) + limits + `horizontalEnabled`, primes
`currentTwist`/`currentElevation`/analog axes to 0, and installs
`TorsoSimulation` as the per-frame Performance (the replicant copy is
console-driven via `TorsoCopySimulation`, still staged).
- `analogElevationAxis` / `analogTwistAxis` members + `SetAnalogElevationAxis` /
`SetAnalogTwistAxis` setters (written by `MechControlsMapper::InterpretControls`).
- `CurrentTwist()` / `CurrentElevation()` / `GetHorizontalEnabled()` accessors.
- `TorsoSimulation(Scalar)` — per-frame aim integration (authentic core):
- `currentElevation += analogElevationAxis · baseElevationRate · dt`, clamped
to [verticalLimitBottom, verticalLimitTop];
- if `horizontalEnabled`: `currentTwist += analogTwistAxis · baseTwistRate ·
dt`, clamped to [horizontalLimitLeft, horizontalLimitRight].
**VERIFIED** (`BT_MECH_LOG` `[mppr] torsoElev=`, forced-input `BT_FORCE_ELEV`):
stick pitch 0.8 slews the elevation up and clamps at the authentic
`verticalLimitTop` = 0.349 rad = **20°**. Zero Fail.
## Deferred (skeleton-link + render wave)
`TorsoSimulation` advances the SCALAR aim state (elevation/twist), which the HUD
reticle / weapon-aim read. APPLYING those angles onto the skeleton — rotating
the `horizontalJointNode` (torso twist) and the elevation joint so the model's
torso/gun barrels visibly aim — is deferred with the skeleton-joint-link +
renderer wave (the joint handles are resolved null for now). `TorsoCopySimulation`
(replicant console-driven aim) also stays staged.