BT410 Phase 5.3.6: look-button state machine -- full eyepoint composition

Reconstructs the binary's five-state LOOK machine (controls mapper tail,
part_013.c:396-459) as proper members/methods:

- MECHMPPR: LookState enum (None/Left/Right/Behind/Down) + lookState/
  previousLookState (out of reserved[24] -> [22]). InterpretControls picks the
  state from the look buttons each frame and on a CHANGE calls
  Mech::CommitLookState. BT_FORCE_LOOK=<1..4> dev hook.
- MECH: authored look angles lookLeft/Right/Front/BackAngle (deg->rad from the
  GameModel resource, guarded) + lookPitch/lookYaw + CommitLookState(int):
  side looks yaw by the authored angle, look-behind = yaw pi + lookBackAngle
  pitch, look-down = lookFrontAngle pitch, forward = identity. The per-frame
  eyepoint compose in Simulate adds the live Torso elevation on top.
  (reservedState [208] -> [202].)
- Deferred inside the commit (needs MechWeapon viewFireEnable/rearFiring):
  per-view weapon fire re-arm + HUD pip group-mask flip.

Verified headlessly: model reads clean authored angles (L=60 R=-60 F=-10 B=0
deg -- ModelResource layout confirmed again); BT_FORCE_LOOK=3 fires ONE commit
([look] state=3 yaw=3.14159 pitch=0) and the per-frame compose holds eyeYaw=pi
with eyePitch=0.349066 (lookBackAngle + the 20deg-clamped elevation). Zero Fail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-21 22:08:50 -05:00
co-authored by Claude Fable 5
parent 87b1c048cf
commit eb8bba3195
6 changed files with 211 additions and 17 deletions
+96 -5
View File
@@ -163,9 +163,20 @@ Mech::Mech(
currentBodySpeed = 0.0f;
eyepointRotation = EulerAngles::Identity;
lookPitch = 0.0f;
lookYaw = 0.0f;
//
// Look-view angles: defaults until the GameModel read below overrides them
// with the authored per-mech values.
//
lookLeftAngle = 90.0f * RAD_PER_DEG;
lookRightAngle = -90.0f * RAD_PER_DEG;
lookFrontAngle = -30.0f * RAD_PER_DEG;
lookBackAngle = 0.0f;
{
for (int i = 0; i < 208; ++i)
for (int i = 0; i < 202; ++i)
{
reservedState[i] = 0;
}
@@ -387,6 +398,10 @@ Mech::Mech(
<< " maxAcc=" << model->maxAcceleration
<< " throttleAdj=" << model->throttleAdjustment
<< " (deg,deg,u/s^2,scale)" << endl << flush;
DEBUG_STREAM << "[mech] look angles: L="
<< model->lookLeftAngle << " R=" << model->lookRightAngle
<< " F=" << model->lookFrontAngle << " B=" << model->lookBackAngle
<< " (deg)" << endl << flush;
}
if (model->walkingTurnRate > 1.0f && model->walkingTurnRate < 360.0f)
{
@@ -404,6 +419,22 @@ Mech::Mech(
{
forwardThrottleScale = model->throttleAdjustment;
}
//
// The authored look-view angles (deg->rad), same insanity band
// as the rest of the guarded reads.
//
{
Scalar a;
a = model->lookLeftAngle;
if (a > -360.0f && a < 360.0f) lookLeftAngle = a * RAD_PER_DEG;
a = model->lookRightAngle;
if (a > -360.0f && a < 360.0f) lookRightAngle = a * RAD_PER_DEG;
a = model->lookFrontAngle;
if (a > -360.0f && a < 360.0f) lookFrontAngle = a * RAD_PER_DEG;
a = model->lookBackAngle;
if (a > -360.0f && a < 360.0f) lookBackAngle = a * RAD_PER_DEG;
}
}
modelDesc->Unlock();
}
@@ -477,6 +508,67 @@ Joint*
return joints->GetJoint(segment->GetJointIndex());
}
//
//#############################################################################
// CommitLookState -- the look-button eyepoint commit (the binary's five-state
// look machine tail, controls mapper part_013.c:396-459). Re-aims the eyepoint
// from the model's authored look angles: side looks yaw by lookLeft/RightAngle,
// look-behind is yaw pi with lookBackAngle pitch, look-down pitches by
// lookFrontAngle, forward is identity. The committed pitch/yaw are stored in
// lookPitch/lookYaw so the per-frame compose in Simulate can keep adding the
// live Torso elevation on top.
//
// Also part of the authentic commit, deferred to the weapon wave: re-arming
// each weapon's view-fire enable (forward view = the non-rear-mounted weapons,
// look-back = the rear-mounted ones, side/down = none) and flipping the HUD pip
// group mask (forward = front group, look-back = rear group) -- both need the
// MechWeapon viewFireEnable/rearFiring members, not yet reconstructed.
//#############################################################################
//
void
Mech::CommitLookState(int look_state)
{
Check(this);
Scalar pitch = 0.0f;
Scalar yaw = 0.0f;
switch (look_state)
{
case MechControlsMapper::LookLeftState:
yaw = lookLeftAngle;
break;
case MechControlsMapper::LookRightState:
yaw = lookRightAngle;
break;
case MechControlsMapper::LookBehindState:
yaw = PI;
pitch = lookBackAngle;
break;
case MechControlsMapper::LookDownState:
pitch = lookFrontAngle;
break;
default:
break; // LookNone: identity
}
lookPitch = pitch;
lookYaw = yaw;
eyepointRotation = EulerAngles(
Radian(Radian::Normalize(pitch)),
Radian(Radian::Normalize(yaw)),
Radian(0.0f)
);
if (getenv("BT_MECH_LOG"))
{
DEBUG_STREAM << "[look] state=" << look_state
<< " yaw=" << yaw << " pitch=" << pitch << endl << flush;
}
Check_Fpu();
}
//
//#############################################################################
// Simulate -- the mech's per-frame body Performance (the Mover locomotion tick).
@@ -625,14 +717,12 @@ void
// Eyepoint / aim-ray composition (mech4.cpp @~5219, pixel-calibrated in the
// BT411 reverse-engineering). The pilot's torso-elevation aim does NOT tilt
// any skeleton joint on this mech family -- it pitches the cockpit eye and
// the weapon boresight directly. Compose the look-state pitch/yaw (0 until
// the look/eyepoint wave reconstructs BTCommitLookState) with the Torso's
// the weapon boresight directly. Compose the committed look-state pitch/yaw
// (CommitLookState, driven by the look buttons) with the Torso's live
// currentElevation. DPLEyeRenderable / the aim ray read this each frame.
//-----------------------------------------------------------------------
//
{
Scalar lookPitch = 0.0f;
Scalar lookYaw = 0.0f;
Scalar elevation = 0.0f;
if (sinkSourceSubsystem != NULL)
{
@@ -673,6 +763,7 @@ void
<< " yaw=" << (Scalar)ypr.yaw
<< " spd=" << currentBodySpeed
<< " eyePitch=" << (Scalar)eyepointRotation.pitch
<< " eyeYaw=" << (Scalar)eyepointRotation.yaw
<< endl << flush;
}
}
+22 -3
View File
@@ -323,6 +323,14 @@
const EulerAngles&
GetEyepointRotation() const { Check(this); return eyepointRotation; }
//
// Look-state commit (called by the controls mapper on a look-button
// state change): re-aim the eyepoint from the model's authored look
// angles. look_state = MechControlsMapper::LookState.
//
void
CommitLookState(int look_state);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local Data
//
@@ -384,17 +392,28 @@
Scalar currentBodySpeed;
//
// Composed each frame in Simulate from the Torso elevation (+ the
// look-state pitch/yaw once the look/eyepoint wave lands).
// Composed each frame in Simulate: the look-state eye component
// (lookPitch/lookYaw, set by CommitLookState from the authored look
// angles) plus the Torso elevation.
//
EulerAngles eyepointRotation;
Scalar lookPitch;
Scalar lookYaw;
//
// The model's authored look-view angles (rad; deg in the resource).
//
Scalar lookLeftAngle;
Scalar lookRightAngle;
Scalar lookFrontAngle;
Scalar lookBackAngle;
//
// Remaining per-frame targeting / animation state, advanced by the
// mech2/mech3/mech4 simulation. Reserved until that path is
// reconstructed with named fields.
//
int reservedState[208];
int reservedState[202];
};
#endif
+31 -3
View File
@@ -286,6 +286,34 @@ wave (`BTCommitLookState`) is reconstructed.
resource limit) and `eyePitch=0.349066` — an exact match, every frame; neutral →
`eyePitch=0`. Zero Fail.
Next: the look-button wave (`BTCommitLookState`, `gBTLookPitch/Yaw`) to compose
the full eyepoint; then the gait leg-clip animation. Both become VISIBLE only
under the renderer; headlessly the composed angles are loggable (as here).
## PHASE 5.3 INCREMENT 6: the look-button state machine (2026-07-21)
The five-state LOOK machine (the binary's controls-mapper tail,
part_013.c:396-459) is reconstructed as proper members/methods (not BT411's
global bridges):
- **Mapper** (`MECHMPPR`): `LookState` enum (None/Left/Right/Behind/Down),
`lookState`/`previousLookState` members; InterpretControls picks the state
from the look buttons each frame and, ON A CHANGE, calls
`mech->CommitLookState(state)`. `BT_FORCE_LOOK=<1..4>` dev hook.
- **Mech**: authored look angles (`lookLeft/Right/Front/BackAngle`, deg→rad
from the GameModel resource, guarded like the other reads) + `lookPitch`/
`lookYaw` (the committed eye component) + **`CommitLookState(int)`** — side
looks yaw by the authored angle, look-behind = yaw π + `lookBackAngle` pitch,
look-down = `lookFrontAngle` pitch, forward = identity. The per-frame
eyepoint compose in Simulate now adds the live Torso elevation on top of the
committed look terms.
- Deferred inside the commit (needs the MechWeapon viewFireEnable/rearFiring
members): re-arming the per-view weapon fire enables (forward = non-rear
weapons, look-back = rear-mounted 'b'-port weapons, side/down = none) and the
HUD pip group-mask flip.
**VERIFIED headlessly:** the model reads clean authored angles (L=60° R=60°
F=10° B=0° — layout confirmed again); `BT_FORCE_LOOK=3` fires ONE commit
(`[look] state=3 yaw=3.14159 pitch=0`) and the per-frame compose holds
`eyeYaw=π` with `eyePitch=0.349066` (lookBackAngle 0 + the 20°-clamped
elevation). Zero Fail.
Next: the gait leg-clip animation / terrain drop / weapon wave. These become
VISIBLE only under the renderer; headlessly the composed angles are loggable
(as here).
+30 -1
View File
@@ -119,8 +119,10 @@ MechControlsMapper::MechControlsMapper(
displayMode = 0;
pilotArrayPage = 0;
pilotArray = 0;
lookState = LookNone;
previousLookState = LookNone;
{
for (int i = 0; i < 24; ++i)
for (int i = 0; i < 22; ++i)
{
reserved[i] = 0;
}
@@ -193,6 +195,15 @@ void
{
stickPosition.y = (Scalar)atof(force_elev);
}
const char *force_look = getenv("BT_FORCE_LOOK");
if (force_look != NULL)
{
int state = atoi(force_look);
lookLeft = (state == (int)LookLeftState) ? 1 : 0;
lookRight = (state == (int)LookRightState) ? 1 : 0;
lookBehind = (state == (int)LookBehindState) ? 1 : 0;
lookDown = (state == (int)LookDownState) ? 1 : 0;
}
}
Torso *torso = (Torso *)mech->GetTorsoSubsystem();
@@ -271,6 +282,24 @@ void
}
}
//
// Look / eyepoint selection. Choose a look direction from the buttons;
// when it changes, commit it -- the mech re-aims the eyepoint from its
// authored look angles (and, once the weapon wave lands, re-arms the
// per-view weapon fire enables through the same commit).
//
previousLookState = lookState;
if (lookLeft > 0) lookState = LookLeftState;
else if (lookRight > 0) lookState = LookRightState;
else if (lookBehind > 0) lookState = LookBehindState;
else if (lookDown > 0) lookState = LookDownState;
else lookState = LookNone;
if (lookState != previousLookState)
{
mech->CommitLookState(lookState);
}
if (getenv("BT_MECH_LOG"))
{
static Scalar reportAccum = 0.0f;
+18 -1
View File
@@ -91,6 +91,21 @@
VeteranMode
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Look states. A five-state machine off the pod's look buttons; on a state
// change InterpretControls calls Mech::CommitLookState, which re-aims the
// eyepoint from the model's authored look angles (and, once the weapon wave
// lands, re-arms the per-view weapon fire enables).
//
public:
enum LookState {
LookNone = 0,
LookLeftState,
LookRightState,
LookBehindState,
LookDownState
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction and Destruction
//
@@ -158,7 +173,9 @@
int displayMode;
int pilotArrayPage;
int pilotArray;
int reserved[24];
int lookState;
int previousLookState;
int reserved[22];
};
#endif
+14 -4
View File
@@ -44,11 +44,21 @@ 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.
## Look-button state machine wired (2026-07-21)
The five-state LOOK machine is live: `LookState` enum + `lookState`/
`previousLookState` members; each frame InterpretControls picks the state from
the look buttons (left/right/behind/down, priority in that order) and on a
CHANGE calls `Mech::CommitLookState(state)`, which re-aims the eyepoint from
the mech's authored look angles (see MECH.NOTES.md increment 6).
`BT_FORCE_LOOK=<1..4>` dev hook. Verified: look-behind commits yaw=π once and
the per-frame eyepoint compose carries it with the live elevation.
## 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).
weapon-side half of the look commit (per-view fire enables + HUD pip group
mask — needs the MechWeapon viewFireEnable/rearFiring members). The
skeleton-joint application of the torso angles is deferred with the render
wave (TORSO.NOTES.md).