From eb8bba319507b213872ad2f849fe6829c00dab58 Mon Sep 17 00:00:00 2001 From: Cyd Date: Tue, 21 Jul 2026 22:08:50 -0500 Subject: [PATCH] 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 --- restoration/source410/BT/MECH.CPP | 101 ++++++++++++++++++++- restoration/source410/BT/MECH.HPP | 25 ++++- restoration/source410/BT/MECH.NOTES.md | 34 ++++++- restoration/source410/BT/MECHMPPR.CPP | 31 ++++++- restoration/source410/BT/MECHMPPR.HPP | 19 +++- restoration/source410/BT/MECHMPPR.NOTES.md | 18 +++- 6 files changed, 211 insertions(+), 17 deletions(-) diff --git a/restoration/source410/BT/MECH.CPP b/restoration/source410/BT/MECH.CPP index 72a9caa5..6536d66d 100644 --- a/restoration/source410/BT/MECH.CPP +++ b/restoration/source410/BT/MECH.CPP @@ -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; } } diff --git a/restoration/source410/BT/MECH.HPP b/restoration/source410/BT/MECH.HPP index db72d5df..9defb9b8 100644 --- a/restoration/source410/BT/MECH.HPP +++ b/restoration/source410/BT/MECH.HPP @@ -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 diff --git a/restoration/source410/BT/MECH.NOTES.md b/restoration/source410/BT/MECH.NOTES.md index fff41969..218ac863 100644 --- a/restoration/source410/BT/MECH.NOTES.md +++ b/restoration/source410/BT/MECH.NOTES.md @@ -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). diff --git a/restoration/source410/BT/MECHMPPR.CPP b/restoration/source410/BT/MECHMPPR.CPP index 3c98a406..c20147d5 100644 --- a/restoration/source410/BT/MECHMPPR.CPP +++ b/restoration/source410/BT/MECHMPPR.CPP @@ -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; diff --git a/restoration/source410/BT/MECHMPPR.HPP b/restoration/source410/BT/MECHMPPR.HPP index 83b2d78a..dfe3ee61 100644 --- a/restoration/source410/BT/MECHMPPR.HPP +++ b/restoration/source410/BT/MECHMPPR.HPP @@ -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 diff --git a/restoration/source410/BT/MECHMPPR.NOTES.md b/restoration/source410/BT/MECHMPPR.NOTES.md index 5a5ef7fe..c69f8ca6 100644 --- a/restoration/source410/BT/MECHMPPR.NOTES.md +++ b/restoration/source410/BT/MECHMPPR.NOTES.md @@ -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).